天天看点

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