【问题标题】:How to use JAXB 2.2.11 in osgi (Liferay DXP)如何在 osgi (Liferay DXP) 中使用 JAXB 2.2.11
【发布时间】:2018-03-15 05:06:39
【问题描述】:

我正在尝试在 osgi 环境 (Liferay DXP) 中使用 JAXB 2.2.11。我在创建 JAXBContext 时遇到问题。根据在研究时发现的一些其他来源,如thisthis,我确定在 osgi 容器中我需要为 JAXB 提供正确的类加载器来实例化上下文。所以我有这样的代码:

ClassLoader cl package.with.jaxb.objects.ObjectFactory.class.getClassLoader(); JAXBContext jc = JAXBContext.newInstance("package.with.jaxb.objects ", cl);

此代码导致空指针异常,堆栈跟踪如下:

Caused by: java.lang.NullPointerException
    at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:129)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:201)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:146)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:371)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:446)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:409)

查看ContextFinder 的源代码我可以看到context 在第129 行必须为空:

throw handleClassCastException(context.getClass(), JAXBContext.class);

我认为问题可能是我的模块依赖于 jaxb-api 2.2.11,但 jaxb-impl 类由 rt.jar 在运行时提供,并且可能比 2.2.11 更新,因为 Liferay DXP 在 JDK 上运行1.8.为了解决这个问题,我尝试在我的 osgi 模块中包含 jaxb-impl.jar 2.2.11 作为依赖项,然后认为 jaxb-api 和 jaxb-impl 版本会匹配。之后,尝试使用与上面相同的代码创建 JAXBContent 会导致以下错误:

ClassCastException: attempting to cast jar:file:/C:/Program%20Files/Java/jdk1.8.0_144/jre/lib/rt.jar!/javax/xml/bind/JAXBContext.class to bundleresource://623.fwk616113009:13/javax/xml/bind/JAXBContext.class. Please make sure that you are specifying the proper ClassLoader.

从这条消息看来,被实例化的 JAXBContext 来自通过 rt.jar 加载的 JAXBContext 版本。这让我很困惑,因为我希望使用由我的模块的类加载器加载的 JAXBContext 版本,因为我已经在我的模块中包含了 jaxb-impl.jar 并且我已经指定了我的模块的类加载器是用于我对 JAXBContext.newInstance 的调用。谁能阐明我如何让 jaxb 2.2.11 在 osgi 容器中工作?

*请注意,我无法升级模块使用的 jaxb-api 版本,因为 JAXB 代码实际上位于需要 jaxb 2.2.11 的第 3 方 jar 中(我刚刚从现在通过编写一些测试 JAXB 代码来计算公式)。

【问题讨论】:

    标签: jaxb osgi osgi-bundle liferay-7


    【解决方案1】:

    这是使用 JDK 11、Liferay DXP/7.2、OSGI 以及从 Dev Studio 创建的示例 Jax-RS Web 服务对我有用的解决方法。我在尝试访问网络服务时遇到的错误如下:

    JAXBException 发生:未找到 JAXB-API 的实现 在模块路径或类路径上.. com.sun.xml.internal.bind.v2.ContextFactory 找不到 org.apache.aries.jax.rs.whiteboard_1.0.4.

    对我有用的是在系统级别定义上下文工厂以覆盖预定义的上下文工厂。将以下系统变量添加到您的系统中

    javax.xml.bind.JAXBContextFactory=com.sun.xml.bind.v2.ContextFactory
    

    例如,您可以将其添加到 Tomcat 的 setenv.sh/bat 文件中,或者在 eclipse 中,您可以在 VM 参数下访问服务器启动配置、参数选项卡

    -Djavax.xml.bind.JAXBContextFactory=com.sun.xml.bind.v2.ContextFactory
    

    这无需添加任何额外的库即可工作,因为 Liferay 已经包含了这些库。

    这是如何工作的?请参阅javadoc for JaxBContext 并阅读发现 JAXB 实现部分。使用 /META-INF/services/javax.xml.bind.JAXBContext 文件对我不起作用。

    我希望这对某人有所帮助。 DXP 用户最后要注意的是,如果您的服务被拒绝,那么您需要阅读Service Access Policies

    【讨论】:

      【解决方案2】:

      经过广泛研究,我找到了以下解决方案。由于似乎通过this post 中接受的答案所建议的通过捆绑类加载器必须是正确的,所以我遵循了弄清楚为什么我在尝试时得到 NullPointerException 的路径。在仔细查看 jaxb-api 的源代码以跟踪 NullPointerException 的堆栈跟踪后,我可以看到 jaxb-api 代码执行类似

      的操作

      classLoader.loadClass("com.sun.xml.internal.bind.v2.ContextFactory")

      classLoader 是我的包的类加载器(因为这是我传入的),ContextFactory 实际上是 jaxb-impl 中的一个类,由引导类加载器加载。这就是问题所在,因为我的 bundle 的类加载器将无法看到引导类加载器加载的类。这让我很困惑,因为我不习惯类加载器在 osgi 中的工作方式。我错误地认为引导类加载器加载的类是可见的,因为我习惯于在有委托的地方加载 Web 应用程序类。在 osgi 类加载器中 彼此完全隔离,只有导出后才能看到。为了解决这个问题,我发现 some helpful posts 在谈论类似的问题。原来有一个概念 在 osgi 中称为引导委托,您可以在其中指定要始终通过引导类加载器加载的类/包的列表。所以最终的结果是两个步骤:

      1) 在调用代码获取 JAXBContext 之前,将线程的类加载器切换到你的包类加载器:

      ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
      
      try {           
          // ObjectFactory here is in the same package as my classes to be marshalled
          ClassLoader objectFactoryClassLoader = ObjectFactory.class.getClassLoader();            
          Thread.currentThread().setContextClassLoader(objectFactoryClassLoader);
          // JAXB code goes here
      } finally {
          Thread.currentThread().setContextClassLoader(currentClassLoader);
      }
      

      2) 使用引导委托机制指定要加载的包。此列表需要包括您需要加载的类的传递依赖项。就我而言,我使用的是 Liferay,所以列表 特定于 Liferay,它位于 portal-ext.properties 配置文件中。幸运的是,我找到了 this post,有人为我完成了大部分工作:

      module.framework.properties.org.osgi.framework.bootdelegation=\
        __redirected,\
        com.liferay.aspectj,\
        com.liferay.aspectj.*,\
        com.liferay.portal.servlet.delegate,\
        com.liferay.portal.servlet.delegate*,\
        com.sun.ccpp,\
        com.sun.ccpp.*,\
        com.sun.crypto.*,\
        com.sun.image.*,\
        com.sun.jmx.*,\
        com.sun.jna,\
        com.sun.jndi.*,\
        com.sun.mail.*,\
        com.sun.management.*,\
        com.sun.media.*,\
        com.sun.msv.*,\
        com.sun.org.*,\
        com.sun.syndication,\
        com.sun.tools.*,\
        com.sun.xml.*,\
        com.yourkit.*,\
        org.eclipse.persistence.internal.jaxb,\
        org.eclipse.persistence.internal.jaxb.*,\
        javax.xml.*,\
        sun.*
      

      有用的链接:

      Why can't JAXB find my jaxb.index when running inside Apache Felix?

      What is the difference between bootdelegation and DynamicImport-Package in osgi

      https://web.liferay.com/web/user.26526/blog/-/blogs/liferay-dxp-and-weblogic-

      https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/bundle-classloading-flow

      http://apache-felix.18485.x6.nabble.com/Classloading-for-JAXB-td4834670.html

      【讨论】:

        【解决方案3】:

        了解它应该如何完成的最佳位置是 Apache Karaf。它不安装任何 JAXB-API 包 - 而是在 lib/endorsed 目录中使用 org.apache.servicemix.specs.jaxb-api-2.2-2.7.0.jar

        这样你就不会使用rt.jar提供的JAXB-API。

        对于实施 - 最好使用 JAXB 包的 ServiceMix 版本:

        • org.apache.servicemix.bundles:org.apache.servicemix.bundles.jaxb-impl:2.2.11_1
        • org.apache.servicemix.bundles:org.apache.servicemix.bundles.jaxb-xjc:2.2.11_1

        【讨论】:

        • 感谢您回答 Grzegorz。不幸的是,正如我在帖子末尾提到的那样,我实际上无法控制 JAXB 代码的编写方式,因为它位于我使用的名为 STIX (github.com/STIXProject/java-stix) 的第 3 方库中,而 STIX 库正在使用 jaxb -api.
        猜你喜欢
        • 1970-01-01
        • 2018-03-02
        • 1970-01-01
        • 2018-04-10
        • 1970-01-01
        • 2019-03-13
        • 2019-01-12
        • 1970-01-01
        • 2018-05-22
        相关资源
        最近更新 更多