原来我们的程序都是基于Equinox架构的,但是后面因为要实现打成war包在中间件中部署的需求,使用了eclipse官方提供的桥接方式实现。
桥接的部分后面有时间了我专门写一个文章来说,不明白的暂时请参考eclipse官方文档。这里主要说一下已经桥接成功,但是在使用CXF时遇到问题的情况。
本来在其他中间件里跑得好好的程序,一放到websphere_v8里,就各种报错,都是与axis2有关的,但是我们的项目并没有使用axis2,而是使用cxf。
报错类似如下(我有3个环境,每个报的错都不同,不过都很明显的出现了不该出现的axis2):
java.lang.ClassCastException: org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler incompatible with org.apache.cxf.frontend.ClientProxy at org.apache.cxf.frontend.ClientProxy.getClient(ClientProxy.java:93)
后来查了资料发现websphere有自带的jaxws引擎。可以看到在$WAS_HOME/endorsed_apis下有三个jar包:
javax.j2ee.annotation.jar jaxb-api.jar jaxws-api.jar//version:2.2
然后在$WAS_HOME/plugins下有org.apache.axis2.jar.
比如说,你实现了MyService继承了javax.xml.Service。
那么在Service的构造方法里我们可以看到:
protected Service(java.net.URL wsdlDocumentLocation, QName serviceName) { delegate = Provider.provider().createServiceDelegate(wsdlDocumentLocation, serviceName, this.getClass()); }
就是这个Provider.provider()得到的Provider实现始终是axis的实现,而不是我们的cxf的实现。
一、通用方案
查询了cxf和IBM的官方文档,得到的解决方案如下:
参考
和
1.关闭websphere自带的jaxws引擎。这里有两种级别的设置:
server级:
在控制台界面进入应用程序服务器 > server1 > 进程定义 > Java 虚拟机,然后在通用JVM参数中加入
-Dcom.ibm.websphere.webservices.DisableIBMJAXWSEngine=true
或者再进入定制属性,添加一个定制属性name=com.ibm.websphere.webservices.DisableIBMJAXWSEngine, value=true.
针对某个app:
在你的war包的META-INF/MANIFEST.MF中加入DisableIBMJAXWSEngine:true,像这样
Manifest-Version: 1.0 DisableIBMJAXWSEngine: true Class-Path:
2.设置你的应用的ClassLoader策略为Parent_Last
企业应用程序 > $YOUR_APP > 类装入和更新检测
把类装入器顺序设置为Parent_Last
企业应用程序 > $YOUR_APP > 模块管理 > $YOUR_MODULE ,将类装入器顺序设置为Parent_Last
最后还有一个地方的类加载策略(可以验证一下这个是否需要设置,有些文章上没有说这个地方):
应用程序服务器 > server1,把类装入方式设置为Parent_Last
以上就是IBM和CXF官方提供的解决方案。
然后在网上还发现了一些其他方案:
1. 移除org.apache.axis2.jarH或移除org.apache.axis2.jar中的‘META-INF/services/javax.xml.ws.spi.Provider‘
这比较暴力,而且会造成对整个WAS的影响。
2. 改用
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setAddress(wsdlURL); factory.setServiceClass(MyWebService.class); MyWebService port = (MyWebService) factory.create(); //and then call ClientProxy as usual but with the object created using JaxWsProxyFactoryBean Client client = ClientProxy.getClient(port);
替代原来的
MyWebService ss = new MyWebService (wsdlURL, SERVICE_NAME); instance = ss.getTestHttpPort(); Client cxfClient = ClientProxy.getClient(instance);
第2条我试的时候,报了MyWebService不是一个interface的错误.没有具体去跟是什么原因。
在网上的所有能找到的方式都尝试无果之后,不得不自己分析了。
二、桥接的方式特殊的地方
JAX-WS采用了service provider interface (SPI)的机制,定义了上层的API,运行的时候再由Provider类加载不同的实现。
首先看一下JAX-WS的加载顺序:
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)
jaxws-api肯定是用的websphere的了,在我们已经按照官方文档关闭了IBMJAXWSEngine和修改ClassLoader策略之后,他还是老是加载到websphere自带的axis2,,所以猜测Provider.provider()在寻找Provider的实现时,根本没有发现我们应用中的cxf包,这跟OSGI桥接的方式相关,使用这种方式的同学应该了解是什么目录结构。所以,解决方式是在war/WEB-INF/lib中也放入一个cxf.jar.
然后再也没有出现axis2来困扰我们了。
这里就会产生一个问题,lib包中有一个cxf,eclipse的plugin目录下也有一个cxf。那么使用的时候会出现类冲突吗?
如果直接把eclipse的plugin目录下的cxf移除掉,启动的时候肯定各种bundle依赖报错。那么外面一个cxf,里面一个cxf到底会不会产生冲突呢?
答案是不会。
因为我把lib下的cxf包中的内容都删了,只剩下META-INF/services/中的几个文件。居然也可以正常运行。所以可以看出运行中加载到的类应该还都是eclipse的plugins中的cxf中的类. 猜测加载cxf中的类是从equinox中的bundle的classLoader开始加载的,所以就会加载到eclipse中的plugins中的类。如果是用的Module或之上的ClassLoader来加载,那么在lib下只有cxf空包,而没有具体的类的时候,肯定会加载不到这个类。那么既然在这种情况下还能加载到,就说明是从bundle的Classloader开始加载的。
不过为了保险起见,这一个步的最终方案为:
把cxf中的META-INF/services目录拷贝出来打成一个jar包,放在war/WEB-INF/lib目录下.
osgi应用使用桥接的方式打成war包部署在websphere上时遇到的与cxf相关的问题,布布扣,bubuko.com