用原生jdk开发好webservice服务端接口后,和客户端联调的过程中调不通,服务端报错:
java.lang.ExceptionInInitializerError
at com.sun.xml.internal.ws.wsdl.PayloadQNameBasedOperationFinder.getWSDLOperationQName(PayloadQNameBasedOperationFinder.java:140)
at com.sun.xml.internal.ws.wsdl.OperationDispatcher.getWSDLOperationQName(OperationDispatcher.java:76)
at com.sun.xml.internal.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:84)
at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:626)
at com.sun.xml.internal.ws.api.pipe.Fiber._doRun(Fiber.java:585)
at com.sun.xml.internal.ws.api.pipe.Fiber.doRun(Fiber.java:570)
at com.sun.xml.internal.ws.api.pipe.Fiber.runSync(Fiber.java:467)
at com.sun.xml.internal.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:299)
at com.sun.xml.internal.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:593)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handleExchange(WSHttpHandler.java:95)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handle(WSHttpHandler.java:80)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:649)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder$1.run(SOAPFaultBuilder.java:570)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder$1.run(SOAPFaultBuilder.java:566)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createJAXBContext(SOAPFaultBuilder.java:565)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.<clinit>(SOAPFaultBuilder.java:555)
... 21 more
17:44:05.222 [schedulerFactoryBean_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers
网上查了好久,大部分都说是因为jaxb-api.jar和Jaxb-impl,jar冲突导致的,大致有以下几种解决方式:
1.删掉lib目录下的jaxb-api.jar和Jaxb-impl.jar;
2.把jdk升级到1.6以上版本;
3.在工程(我用的maven建的工程)的pom.xml中增加
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<com.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize>true</com.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize>
</systemPropertyVariables>
</configuration>
</plugin>
以上方式我都尝试了但都无效,我用的maven搭建的工程,为此还折腾了各种版本的引用包均无效。
最后,尝试直接在webservice发布时就设置:
System.setProperty("javax.xml.bind.JAXBContext", "com.sun.xml.internal.bind.v2.ContextFactory");
webservice服务器端倒是不报错了,但是客户端依然调不通,报错说找不到匹配的方法。
然后,又是一番折腾,基本上都说是webservice内部jar版本与现在所用的jdk的jar有冲突,问题依然没有解决。
后来发现如果客户端调用的时候设置了QNAME的namespace就可以绕过此错误,附上客户端调用的关键代码:
Call call = null;
try {
call = (Call)service.createCall();
call.setTargetEndpointAddress(new URL("http://localhost:8081/UserService?wsdl"));
call.setProperty("axis.connection.timeout", new Integer(3600000));
call.setTimeout(new Integer(3600000));
call.setOperationName(new javax.xml.namespace.QName("http://webservice.web.demo.com/", "queryUsers")); //用这句可以正常调用
//call.setOperationName("queryUsers");//用这句就报错说找不到匹配的方法
Object res=call.invoke(new Object[]{});
PrintWriter out = response.getWriter();
out.println(res);
}catch (Exception e) {
e.printStackTrace();
}
如果不想改客户端的调用代码,也可添加拦截器拦截webservice请求,拦截后判断namespace是否存在不存在则加上,方法如下:
在classpath下建handler-chain.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<javaee:handler-chain>
<javaee:handler>
<!-- 拦截器类的包名和类名路径 -->
<javaee:handler-class>com.xxx.xxx.web.service.WebservicePhaseInterceptor</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</javaee:handler-chains>
在webservice的实现类上加HandlerChain配置:
@HandlerChain(file="handler-chain.xml")
public class UserWebServiceImpl implements UserWebService {
/**
代码省略
**/
}
SOAPHandler实现拦截请求并加namespace的类:
package com.xxx.xxx.web.service;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.apache.commons.lang3.StringUtils;
public class WebservicePhaseInterceptor implements SOAPHandler<SOAPMessageContext> {
@Override
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage msg = context.getMessage();
SOAPBody body = msg.getSOAPBody();
Iterator<?> iter = body.getChildElements();
while (iter.hasNext()) {
Object next = iter.next();
if (next instanceof SOAPElement) {
SOAPElement se = (SOAPElement) next;
String uri = se.getNamespaceURI();
if (StringUtils.isBlank(uri)) {
QName name = se.getElementQName();
QName qName = new QName("你的namespaceURI,在wsdl文件中可以看到", name.getLocalPart());
se.setElementQName(qName);
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
@Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
@Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
拦截器的实现,参考了 http://blog.csdn.net/accountwcx/article/details/46986943,感谢。
目的虽然达到了,问题也绕过去了,客户端调用成功,但是问题依然没有从根本上解决。我实在想不到好的解决方式了,如果有解决方法还请不吝留言告知哦,感激不尽。