1. 程式人生 > >webservice:com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.a

webservice:com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.a

用原生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,感謝。

目的雖然達到了,問題也繞過去了,客戶端呼叫成功,但是問題依然沒有從根本上解決。我實在想不到好的解決方式了,如果有解決方法還請不吝留言告知哦,感激不盡。