glassfish下cxf無法運行的問題
這是近期工作中遇到的一個問題,cxf在glassfish下timeout設(shè)置出現(xiàn)問題,進而引發(fā)的關(guān)于classloader, JAX-WS的一些小故事,很驚訝的發(fā)現(xiàn)cxf在這種情況下根本沒有辦法運行于glassfish平臺。關(guān)鍵字:glassfish, cxf, classloader, JAX-WS, metro。
首先看問題的發(fā)生,我們有一個webservice的客戶端,使用cxf開發(fā),原來運行于weblogic,目前準(zhǔn)備移植到glassfish。異常測試中發(fā)現(xiàn)timeout設(shè)置不再有效,在glassfish平臺上timeout時間似乎是無限?測試中試過等待10分鐘也沒有timeout,socket一直連著,客戶端一直在等應(yīng)答。
于是準(zhǔn)備增加timeout的設(shè)置到cxf中,下面是cxf的timeout的典型設(shè)置:
Client client = ClientProxy.getClient(this.port);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(30 * 1000);
httpClientPolicy.setReceiveTimeout(60 * 1000);
http.setClient(httpClientPolicy);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(30 * 1000);
httpClientPolicy.setReceiveTimeout(60 * 1000);
http.setClient(httpClientPolicy);
這段代碼在weblogic中是正常工作的,但是在glassfish上就出現(xiàn)問題,“Client client = ClientProxy.getClient(this.port);” 這行會拋出一個異常ClassCastException:
javax.xml.ws.soap.SOAPFaultException: com.sun.xml.ws.client.sei.SEIStub cannot be cast to org.apache.cxf.frontend.ClientProxy
google了一下發(fā)現(xiàn)這個問題的出現(xiàn)已經(jīng)在cxf的issue列表中, CXF-2237:
https://issues.apache.org/jira/browse/CXF-2237
從這個網(wǎng)址得到的信息:
1. That error would mean that the Sun JAX-WS implementation is being picked up instead of the CXF version.
2. Classpath issue picking up wrong JAX-WS implementation.
原因似乎很清晰了,但是沒有給出解決方法,繼續(xù)google發(fā)現(xiàn)類似的問題在jboss平臺也有發(fā)現(xiàn),cxf有給出解決方法。
于是郁悶了,找了找資料,發(fā)現(xiàn)問題可能和JAX-WS有關(guān):
1. JAX-WS 是用于web service的java api,定義在JSR 224.
2. sun提供了JAX-WS的一個實現(xiàn)Metro,包含在glassfish中。
3. sun的實現(xiàn)后來加入了jdk6(不過package和Metro不同)
4. apache cxf 是另外一個JAX-WS實現(xiàn)
從上面的異常看,"com.sun.xml.ws.client.sei.SEIStub cannot be cast to org.apache.cxf.frontend.ClientProxy", 像是從"sun impl" -> "cxf impl"的轉(zhuǎn)換出問題,也就是說runtime時實際運行的是sun的JAX-WS實現(xiàn),而不是cxf的實現(xiàn)。這個和CXF-2237的描述是一致的,因此問題基本定位出來了:glassfish中cxf的實現(xiàn)沒有被裝載成功。
隨即查找了一下關(guān)于JAX-WS 實現(xiàn)裝載的資料,application是可以通過Provider SPI來選擇不同的JAX-WS實現(xiàn)的,在JSR 224: JAX-WS 2.x 中的chapter 6.2.1 有選擇Provider implementation的規(guī)則:
1. If a resource with the name of META-INF/services/javax.xml.ws.spi.Provider exists, then its first line, if present, is used as the UTF-8 encoded name of the implementation class.
意思是說查找名為META-INF/services/javax.xml.ws.spi.Provider的資源,如果存在,那么它的第一行,就是實現(xiàn)類的名字。
我們試著打開cxf的jar文件,發(fā)現(xiàn)的確有上述文件,內(nèi)容只有一行:org.apache.cxf.jaxws.spi.ProviderImpl。然后查找了一下glassfish的jar文件,在glassfish安裝目錄下的lib\webservice-rt.jar中找到了JAX-WS的實現(xiàn),同樣有META-INF/services/javax.xml.ws.spi.Provider文件,內(nèi)容為: com.sun.xml.ws.spi.ProviderImpl.
2. If the ${java.home}/lib/jaxws.properties file exists and it is readable by the java.util.Properties.load(InputStream) method and it contains an entry whose key
is javax.xml.ws.spi.Provider, then the value of that entry is used as the name of the implementation class.
類似的,通過${java.home}/lib/jaxws.properties 文件來設(shè)置。
3. If a system property with the name javax.xml.ws.spi.Provider is defined, then its value is used as the name of the implementation class.
通過系統(tǒng)屬性javax.xml.ws.spi.Provider來設(shè)置。
4. Finally, a default implementation class name is used.
最后,默認(rèn)實現(xiàn),即使就是jdk中的sun的實現(xiàn),猜測是Metro的某個版本
從上述規(guī)則來看,明顯當(dāng)前是遵循了第一個規(guī)則,從資源META-INF/services/javax.xml.ws.spi.Provider中讀取實現(xiàn)類。而且雖然application中有cxf.jar的存在并且有名為META-INF/services/javax.xml.ws.spi.Provider的資源,但是因為application的classloader在裝載資源時,按照標(biāo)準(zhǔn)的classloader機制,會首先代理給parent classloader,因此最后實際是system classloader首先嘗試裝載資源,glassfish下lib\webservice-rt.jar是屬于system classpath,glashfish的system classloader會查找到并裝載webservice-rt.jar中的META-INF/services/javax.xml.ws.spi.Provider。這樣application中的classloader就沒有機會裝載cxf的META-INF/services/javax.xml.ws.spi.Provider,而是直接使用glassfish system classloader裝載好的javax.xml.ws.spi.Provider,自然就是Metro的實現(xiàn)。
現(xiàn)在關(guān)鍵的問題在于這個規(guī)則是第一順序位,后面的2,3根本沒有機會。因此想裝載cxf的JAX-WS實現(xiàn),就只有想辦法改變classloader的裝載機制,想辦法讓application中的classloader有機會裝載到cxf的JAX-WS實現(xiàn)。
類似的class 裝載的問題在cxf上就比較常見了,之前在weblogic上也經(jīng)常遇到類似的,解決的思路就是改變classloader的默認(rèn)裝載機制,讓application中的classloader自己去裝載而不是代理給system classloader。
weblogic中對此有專門的設(shè)置prefer-application-packages,在weblogic-ejb-jar.xml或者weblogic-application.xml中加入:
<prefer-application-packages>
<package-name>javax.jws.*</package-name>
</prefer-application-packages>
<package-name>javax.jws.*</package-name>
</prefer-application-packages>
就可以指示weblogic,對于上述的package,優(yōu)先實現(xiàn)application的classloader而不是使用system classloader,從而解決這個問題。
因為問題的解決思路就很明顯了: 在glassfish中找到類似于prefer-application-packages的方法。
首先找到的是glassfish中的delegate設(shè)置,看delegate的介紹:
(optional) If true, the web module follows the standard class loader delegation model and delegates to its parent class loader first before looking in the local class loader. You must set this to true for a web application that accesses EJB components or that acts as a web service client or endpoint.
粗看正是我們想要的,再一看發(fā)現(xiàn)不行,delegate的使用是有限制的:
1. delegate只在sun-web.xml有,只能用于web app,不能用于普通的application
2. delegate是有限制的,對于"java.*"和"javax.*"的包不能生效的
隨后的查找發(fā)現(xiàn),非常遺憾,glassfish沒有提供其他的類似機制,因此試圖改變classloader的想法陷入絕境。
只好先考慮其他的方法,一個思路就是讓glassfish的system classloader直接load到cxf,方法如下:
1.將cxf的lib包(或者只是META-INF/services/javax.xml.ws.spi.Provider文件)放到glassfish的system classpath下并在lib\webservice-rt.jar之前
2.刪除lib\webservice-rt.jar
這個試過了,glassfish直接起不來
但這些都不是足夠妥當(dāng),最后考慮到既然大家都是JAX-WS實現(xiàn),而且sun的Metro也算做的不錯,因此將錯就錯,考慮直接使用Metro好了。就當(dāng)前遇到的timeout設(shè)置的問題,在Metro中是非常容易解決的:
Map<String, Object> requestContext = ((BindingProvider) service).getRequestContext();
requestContext.put("com.sun.xml.ws.connect.timeout", 30 * 1000);
requestContext.put("com.sun.xml.ws.request.timeout", 60 * 1000);
requestContext.put("com.sun.xml.ws.connect.timeout", 30 * 1000);
requestContext.put("com.sun.xml.ws.request.timeout", 60 * 1000);
至此這次timeout的設(shè)置問題總算解決了,但是,依然沒有辦法解決glassfish的classloader問題,以后如果遇到類似的需要application優(yōu)先裝載特定類的情況,還是會遭遇同樣的困境。這里比較奇怪的是,為什么glassfish會沒有類似的設(shè)置,按說既然glassfish已經(jīng)給出了delegate這個設(shè)置,說明glassfish已經(jīng)意思到有這個需要并且也給出了部分解決方法,但是為什么對應(yīng)于application確不給出任何解決方案呢?百思不得其解。
而從上面的描述也可以看出,如果不使用特殊的方法,正常情況下,applicatio是沒有辦法裝載到cxf的JAX-WS實現(xiàn)的,實際在runtime時跑的是metor的實現(xiàn)。
posted on 2010-03-24 14:52 sky ao 閱讀(3150) 評論(0) 編輯 收藏 所屬分類: web service