<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<webservice> wsannotations = new arraylist<webservice>(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 來提前加載了。如下:
<?xml version="1.0" encoding="utf-8"?>
<weblogic-web-app>
<container-descriptor>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
</weblogic-web-app>
總以為能夠象往常一樣,這樣可以了,但是将 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.<clinit>(runtimebuiltinleafinfoimpl.java:186)
at com.sun.xml.bind.v2.model.impl.runtimetypeinfosetimpl.<init>(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.<init>(modelbuilder.java:104)
at com.sun.xml.bind.v2.model.impl.runtimemodelbuilder.<init>(runtimemodelbuilder.java:49)
at com.sun.xml.bind.v2.runtime.jaxbcontextimpl.gettypeinfoset(jaxbcontextimpl.java:372)
at com.sun.xml.bind.v2.runtime.jaxbcontextimpl.<init>(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