问题
项目中使用JAX-WS 2.1.4,抛出以下错误
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.<clinit>(
SOAPFaultBuilder.java:533
)
环境
- JAX-WS 2.1.4
- JDK 1.6.0.22
maven depedency declaration
条件
当server端的web service 抛出异常时,比如NEP,客户端抛出上面的ClassCastException
背景
为什么不使用JDK自带的jax-ws?
因为我们需要将sevice deploy在
tomcat中,要用到com.sun.xml.ws.transport.http.servlet.WSServletContextListener。
而com.sun.xml.ws.transport.http.servlet.* 是J2ee的一部分,并没有发布在JDK中。
起因
JAX-WS provider is loaded from JDK rt.jar com.sun.xml.internal.ws.spi.ProviderImpl,使用com.sun.xml.internal.bind.v2.* 中的class
while JAXB Context is load from Jaxb-impl-2.1.7.jar
com.sun.xml.bind.v2.ContextFactory,不是JDK自带的JAXB
源头
出现上面原因,一种可能性是应用程序的lib中加入了jaxb-impl.jar,而没有引入jaxws-rt的jar
而我们的应用已经引入了jaxws-rt(如前maven declaration)和jaxb-impl.jar,理论上都是从应用程序的依赖包中加载才对。
在灵光一现之后,发现src folder有人放了下面文件,指定JAXB为JDK的实现
|_META-INF
|_services
|_ javax.xml.ws.spi.Provider (com.sun.xml.internal.ws.spi.ProviderImpl)
解决
reomve 掉上面的META-INF folder 即可
总结
JDK中的很多API采用了service provider interface (SPI)的机制,(XML, encryption)
JDK定义上层的contract/API,并提供默认的实现,应用本身可根据需要提供自己的实现。
一般由Provider类加载不同的实现。
JAX-WS 的加载顺序
javax.xml.ws.spi.Provider provider()
- If a resource with the name of META-INF/services/javax.xml.ws.spi.Provider
=
com.sun.xml.ws.spi.ProviderImpl
- $java.home/lib/jaxws.properties,it contains an entry whose key is javax.xml.ws.spi.Provider
- If a system property with the name javax.xml.ws.spi.Provider
- Default is loaded(com.sun.xml.internal.ws.spi.ProviderImpl)
javax.xml.bind.ContextFinder.find
- jaxb.properties (key=javax.xml.bind.JAXBContext)
- System property with name javax.xml.bind.JAXBContext
- META-INF/services/javax.xml.bind.JAXBContext
- Default is loaded(com.sun.xml.internal.bind.v2.ContextFactory)
|_META-INF|_services|_ javax.xml.bind.JAXBContext (com.sun.xml.bind.v2.ContextFactory)