原來我們的程序都是基於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
- $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)
- 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文件夾下.