天天看點

Apache CXF 在 WebLogic 9.2 上的問題定位分析及權宜之計

<a href="http://yulimin.javaeye.com/blog/129015">http://yulimin.javaeye.com/blog/129015</a>

同樣的程式在 tomcat 5.5.16/25 以及 jetty 5.1.12 上都能夠成功部署及運作,但是部署到 weblogic 9.2 上時出錯

org.springframework.web.context.contextloader initwebapplicationcontext

fatal: context initialization failed

org.springframework.beans.factory.beancreationexception: error creating bean with name 'xxxwebservice': invocation of init method failed; nested exception is java.lang.nosuchmethoderror: portname

caused by: java.lang.nosuchmethoderror: portname

at org.apache.cxf.jaxws.support.jaxwsimplementorinfo.getendpointname(jaxwsimplementorinfo.java:154)

at org.apache.cxf.jaxws.support.jaxwsserviceconfiguration.getendpointname(jaxwsserviceconfiguration.java:89)

at org.apache.cxf.service.factory.reflectionservicefactorybean.getendpointname(reflectionservicefactorybean.java:983)

at org.apache.cxf.frontend.abstractendpointfactory.createendpoint(abstractendpointfactory.java:91)

at org.apache.cxf.frontend.serverfactorybean.create(serverfactorybean.java:107)

at org.apache.cxf.jaxws.jaxwsserverfactorybean.create(jaxwsserverfactorybean.java:147)

at jrockit.reflect.virtualnativemethodinvoker.invoke(ljava.lang.object;[ljava.lang.object;)ljava.lang.object;(unknown source)

at java.lang.reflect.method.invoke(ljava.lang.object;[ljava.lang.object;i)ljava.lang.object;(unknown source)

at org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.invokecustominitmethod(abstractautowirecapablebeanfactory.java:1214)

at org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.invokeinitmethods(abstractautowirecapablebeanfactory.java:1179)

at org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.initializebean(abstractautowirecapablebeanfactory.java:1145)

at org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.createbean(abstractautowirecapablebeanfactory.java:427)

at org.springframework.beans.factory.support.abstractbeanfactory$1.getobject(abstractbeanfactory.java:251)

at org.springframework.beans.factory.support.defaultsingletonbeanregistry.getsingleton(defaultsingletonbeanregistry.java:144)

at org.springframework.beans.factory.support.abstractbeanfactory.getbean(abstractbeanfactory.java:248)

at org.springframework.beans.factory.support.abstractbeanfactory.getbean(abstractbeanfactory.java:160)

at org.springframework.beans.factory.support.defaultlistablebeanfactory.preinstantiatesingletons(defaultlistablebeanfactory.java:279)

at org.springframework.context.support.abstractapplicationcontext.refresh(abstractapplicationcontext.java:360)

at org.springframework.web.context.contextloader.createwebapplicationcontext(contextloader.java:241)

at org.springframework.web.context.contextloader.initwebapplicationcontext(contextloader.java:184)

at org.springframework.web.context.contextloaderlistener.contextinitialized(contextloaderlistener.java:49)

從 stack trace 來分析,spring 在 contextloaderlistener 進行加載配置檔案時出錯了。

通過檢視 jaxwsimplementorinfo.java:154 的源程式

代碼

portname = wsannotations.get(x).portname();  

可以知道 wsannotations 的定義為

private list&lt;webservice&gt; wsannotations = new arraylist&lt;webservice&gt;(2);  

得到list裡的對象類型為webservice,并得到全稱為javax.jws.webservice

通過 jarclassfind 來搜尋,可以得到 apache cxf 中的 geronimo-ws-metadata_2.0_spec-1.1.1.jar 包含有這個類

而 weblogic 9.2 中的則有兩個 .jar 檔案包含有此類:

no.1

jar package:%wls_home%/server/lib/api.jar

no.2

jar package:%wls_home%/server/lib/weblogic.jar

經過對比,weblogic 9.2 裡的兩個 .jar 檔案裡帶個類是一樣的,況且,正常啟動時 weblogic 也隻是将 weblogic.jar 加到 classpath 中。

問題猜測應當是由于這兩類的版本不一樣導緻的,拆出來 jad 一下,weblogic 中的 javax.jws.webservice.java 代碼為:

package javax.jws;  

import java.lang.annotation.annotation;  

public interface webservice  

    extends annotation  

{  

    public abstract string name();  

    public abstract string targetnamespace();  

    public abstract string servicename();  

    public abstract string wsdllocation();  

    public abstract string endpointinterface();  

}  

拆出 apache cxf 裡的 javax.jws.webservice jad 一下,代碼為:

    public abstract string portname();  

這下就完全明白了,是 apache cxf 期望加載的 javax.jws.webservice 注解與 weblogic 中 javax.jws.webservice 注解版本不一緻導緻的。

ok,現在首先想到的是在 weblogic.xml 設定 prefer-web-inf-classes 為 true 來提前加載了。如下:

&lt;?xml version="1.0" encoding="utf-8"?&gt;  

&lt;weblogic-web-app&gt;  

    &lt;container-descriptor&gt;  

        &lt;prefer-web-inf-classes&gt;true&lt;/prefer-web-inf-classes&gt;  

    &lt;/container-descriptor&gt;  

&lt;/weblogic-web-app&gt;  

總以為能夠象往常一樣,這樣可以了,但是将 prefer-web-inf-classes 設定為 true 後,卻發生了另一個異常了:

fatal: context initialization failed  

org.springframework.beans.factory.beancreationexception: error creating bean with name 'xxxwebservice': invocation of init method failed; nested exception is java.lang.linkageerror: class javax/xml/namespace/qname violates loader constraints  

caused by: java.lang.linkageerror: class javax/xml/namespace/qname violates loader constraints  

        at com.sun.xml.bind.v2.model.impl.runtimebuiltinleafinfoimpl.&lt;clinit&gt;(runtimebuiltinleafinfoimpl.java:186)  

        at com.sun.xml.bind.v2.model.impl.runtimetypeinfosetimpl.&lt;init&gt;(runtimetypeinfosetimpl.java:25)  

        at com.sun.xml.bind.v2.model.impl.runtimemodelbuilder.createtypeinfoset(runtimemodelbuilder.java:84)  

        at com.sun.xml.bind.v2.model.impl.runtimemodelbuilder.createtypeinfoset(runtimemodelbuilder.java:41)  

        at com.sun.xml.bind.v2.model.impl.modelbuilder.&lt;init&gt;(modelbuilder.java:104)  

        at com.sun.xml.bind.v2.model.impl.runtimemodelbuilder.&lt;init&gt;(runtimemodelbuilder.java:49)  

        at com.sun.xml.bind.v2.runtime.jaxbcontextimpl.gettypeinfoset(jaxbcontextimpl.java:372)  

        at com.sun.xml.bind.v2.runtime.jaxbcontextimpl.&lt;init&gt;(jaxbcontextimpl.java:236)  

        at com.sun.xml.bind.v2.contextfactory.createcontext(contextfactory.java:76)  

        at com.sun.xml.bind.v2.contextfactory.createcontext(contextfactory.java:55)  

        at jrockit.reflect.initialmethodinvoker.invoke(ljava.lang.object;[ljava.lang.object;)ljava.lang.object;(unknown source)  

        at java.lang.reflect.method.invoke(ljava.lang.object;[ljava.lang.object;i)ljava.lang.object;(unknown source)  

        at javax.xml.bind.contextfinder.newinstance(contextfinder.java:210)  

        at javax.xml.bind.contextfinder.find(contextfinder.java:366)  

        at javax.xml.bind.jaxbcontext.newinstance(jaxbcontext.java:574)  

        at org.apache.cxf.jaxb.jaxbdatabinding.createjaxbcontext(jaxbdatabinding.java:377)  

        at org.apache.cxf.jaxb.jaxbdatabinding.initialize(jaxbdatabinding.java:182)  

        at org.apache.cxf.service.factory.reflectionservicefactorybean.buildservicefromclass(reflectionservicefactorybean.java:244)  

        at org.apache.cxf.service.factory.reflectionservicefactorybean.initializeservicemodel(reflectionservicefactorybean.java:272)  

        at org.apache.cxf.service.factory.reflectionservicefactorybean.create(reflectionservicefactorybean.java:146)  

        at org.apache.cxf.jaxws.support.jaxwsservicefactorybean.create(jaxwsservicefactorybean.java:89)  

        at org.apache.cxf.frontend.abstractendpointfactory.createendpoint(abstractendpointfactory.java:83)  

        at org.apache.cxf.frontend.serverfactorybean.create(serverfactorybean.java:107)  

        at org.apache.cxf.jaxws.jaxwsserverfactorybean.create(jaxwsserverfactorybean.java:147)  

面對這個 java.lang.linkageerror: class javax/xml/namespace/qname 錯誤,自然又聯想到了版本不一緻的問題。

繼續 jarclassfind ,在 apache cxf 裡找到 stax-api-1.0.1.jar 包含有這個類。

no.3

jar package:%wls_home%/server/lib/webserviceclient+ssl.jar

no.4

jar package:%wls_home%/server/lib/webserviceclient.jar

no.5

jar package:%wls_home%/server/lib/xbean.jar

同樣與 weblogic 啟動時有關的仍是 weblogic.jar ,怪異的是 weblogic 中居然有三個版本的 javax/xml/namespace/qname 這個類,真是比較糟糕的事情。

api.jar、weblogic.jar 與 webserviceclient+ssl.jar、webserviceclient.jar 和 xbean.jar 中各是一個版本。

但是通過 jad 發現 weblogic 裡的 javax/xml/namespace/qname 與 apache cxf 裡的是一樣的,比較奇怪了。。。

再進行認真地比較發現,檔案的時間不同,再直接進行對比 .class 檔案,還是有兩處有不同之處。apache cxf 的時間晚于 weblogic 裡的類的時間,于是隻能懷疑後者引起類在加載時發生了 java.lang.linkageerror 錯誤。

經查jdk文檔:linkageerror 的子類訓示一個類在一定程度上依賴于另一個類;但是,在編譯前一個類之後,後一個類發生了不相容的改變。

于是真相大明了?因為 apache cxf 裡的類的時間晚于 weblogic 裡的類的時間,而且類的二進制内容也發生了改變,于是原來 weblogic 裡的其它類由于我們通過了 prefer-web-inf-classes 設定為 true 後,而引用到了現在的 apache cxf 裡的類了,就發生了 java.lang.linkageerror 錯誤。

由于設定 prefer-web-inf-classes 設定為 true 後,提前加載了應用程式中 lib 目錄下的 .jar 檔案,而發生了這個問題,于是就嘗試去掉 prefer-web-inf-classes 的設定,直接在 weblogic 的啟動腳本中增加第一次異常時相關的 jar 到 classpath 中,即 geronimo-ws-metadata_2.0_spec-1.1.1.jar 檔案,隻提前加載這個 jar 檔案。

set classpath_cxf=x:/xyz/geronimo-ws-metadata_2.0_spec-1.1.1.jar  

set classpath=%classpath_cxf%;%classpath%;%medrec_weblogic_classpath%  

于是,再次進行啟動,這下則可以正常啟動了。

為了支援 aegis ,除了必需的之外,還需要再增加如下 jar 包:

- jaxen.jar

- jdom.jar

- stax-utils.jar