<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