標籤:osgi websphere cxf axis
原來我們的程式都是基於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.ClientProxyat org.apache.cxf.frontend.ClientProxy.getClient(ClientProxy.java:93)
後來查了資料發現websphere有內建的jaxws引擎。可以看到在$WAS_HOME/endorsed_apis下有三個jar包:
javax.j2ee.annotation.jarjaxb-api.jarjaxws-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的官方文檔,得到的解決方案如下:
參考
http://www-01.ibm.com/support/knowledgecenter/SS7JFU_7.0.0/com.ibm.websphere.express.doc/info/exp/ae/twbs_thirdparty.html?lang=en
和
http://cxf.apache.org/docs/application-server-specific-configuration-guide.html#ApplicationServerSpecificConfigurationGuide-ForWebSphere6.1.0.29+,V7andV8
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.0DisableIBMJAXWSEngine: trueClass-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 JaxWsProxyFactoryBeanClient 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目錄下.