如何在osgi(Liferay DXP)中使用JAXB 2.2.11
我试图在osgi环境(Liferay DXP)中使用JAXB 2.2.11。我在创建JAXBContext时遇到问题。根据在研究和时发现的其他一些源代码,我确定在osgi容器中,我需要为JAXB提供正确的类加载器来实例化上下文。我有这样的代码:如何在osgi(Liferay DXP)中使用JAXB 2.2.11,jaxb,osgi,osgi-bundle,liferay-7,Jaxb,Osgi,Osgi Bundle,Liferay 7,我试图在osgi环境(Liferay DXP)中使用JAXB 2.2.11。我在创建JAXBContext时遇到问题。根据在研究和时发现的其他一些源代码,我确定在osgi容器中,我需要为JAXB提供正确的类加载器来实例化上下文。我有这样的代码: ClassLoader cl package.with.jaxb.objects.ObjectFactory.class.getClassLoader(); JAXBContext jc=JAXBContext.newInstance(“package.
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)
查看源代码,我可以看到第129行的context
必须为空:
抛出handleClassCastException(context.getClass(),JAXBContext.class)代码>
我认为问题可能在于我的模块依赖于jaxb api 2.2.11,但jaxb impl类是由rt.jar在运行时提供的,可能比2.2.11更新,因为Liferay DXP在JDK 1.8上运行。为了解决这个问题,我尝试将jaxb-impl.jar2.2.11作为依赖项包含在我的osgi模块中,并认为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时使用模块的类加载器。有人能告诉我如何让JAXB2.2.11在osgi容器中工作吗
*请注意,我无法升级我的模块使用的jaxb api版本,因为jaxb代码实际上位于需要jaxb 2.2.11的第三方jar中(我刚刚通过编写一些测试jaxb代码从等式中删除了第三方jar)。最好的地方是ApacheKaraf。它不安装任何JAXB-API捆绑包,而是使用org.apache.servicemix.specs.JAXB-API-2.2-2.7.0.jar
内部lib/approved
目录
这样,您就不会使用rt.jar
提供的JAXB-API
对于实现,最好使用ServiceMix版本的JAXB捆绑包:
- bundles:org.apache.servicemix.bundles.jaxb impl:2.2.111
- org.apache.servicemix.bundles:org.apache.servicemix.bundles.jaxbxjc:2.2.111
经过广泛研究,我找到了以下解决方案。因为它看起来像是通过bundle类加载器,正如在中被接受的答案所建议的那样,必须是正确的,所以我沿着这条路径,试图找出为什么我会得到一个NullPointerException。在仔细查看jaxb api的源代码以跟踪NullPointerException的堆栈跟踪之后,我可以看到jaxb api代码做了如下事情
classLoader.loadClass(“com.sun.xml.internal.bind.v2.ContextFactory”)
其中,classLoader
是我的包的类加载器(因为这是我传入的),而ContextFactory
实际上是jaxb impl中的一个类,由引导类加载器加载。这就是问题所在,因为我的
bundle的类加载器将无法看到引导类加载器加载的类。这让我感到很困惑,因为我不习惯osgi中类装入器的工作方式。我错误地认为引导类加载器加载的类是可见的,因为我习惯于在有委托的情况下加载web应用程序类。在osgi类装入器中
它们彼此完全隔离,只有在导出时才可见。为了回避这个问题,我发现自己在谈论类似的问题。原来有一个概念
在osgi中调用boot delegation,您可以指定始终通过引导类加载器加载的类/包的列表。因此,最终结果是两个步骤:
1) 在调用代码获取JAXBContext之前,将线程的类装入器切换到bundle类装入器:
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配置文件中。幸运的是,我找到了有人为我完成大部分工作的地方:
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.*
有用链接:
以下是我使用JDK 11、Liferay DXP/7.2、OSGI以及从Dev Studio创建的示例Jax RS web服务的变通方法。尝试访问web服务时,我遇到的错误如下:
发生JAXBEException:未找到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已经包含了这些库
这是怎么回事?请参阅并阅读JAXB实现的发现部分。使用/META-INF/services/javax.xml.bind.JAXBContext文件对我不起作用
我希望这对某人有帮助。
DXP用户的最后一个注意事项是,如果您的服务被拒绝,那么您需要阅读感谢您回答Grzegorz。不幸的是,正如我在文章末尾提到的,我不知道该怎么做