最近项目上要调用其他系统的WebService(Axis搭建),由于接收数据较大耗时4分钟左右,所以要设置超时时间,而且系统中已有jar包的原因,导致许多方法都不能用,最终用HttpClient。
方法一:使用JaxWsDynamicClientFactory调用WebService
public static Object call(String wsdl,String method,String requestStr){
JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance();
Client client = factory.createClient(wsdl);
HTTPConduit conduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
long timeout = 10 * 60 * 1000;//
policy.setConnectionTimeout(timeout);
policy.setReceiveTimeout(timeout);
conduit.setClient(policy);
//动态invoke方法
Object[] os =client.invoke(method,requestStr);
return os[0];
}
方法二:使用JaxWsProxyFactoryBean调用WebService
原理:使用JaxWsProxyFactoryBean类生成本地的代理类。
首先创建接口,然后通过JaxWsProxyFactoryBean调用。
@WebService
public interface IDataService {
@WebMethod
@WebResult
String getData(@WebParam String requestData);
}
public static String call(String wsdl, String requestStr) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(IDataService.class);
factory.setAddress(wsdl);
IDataService service = (IDataService) factory.create();
// 设置超时时间
org.apache.cxf.endpoint.Client proxy = ClientProxy.getClient(service);
HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
long timeout = 10 * 60 * 1000;//
policy.setConnectionTimeout(timeout);
policy.setReceiveTimeout(timeout);
conduit.setClient(policy);
// 发出请求
return service.getData(requestStr);
}
方法三:使用wsimport命令构建WebService客户端
首先使用wsimport命令构建客户端,然后用下面的方法调用。
public static String call(String requestData) {
// 构建的客户端代码
IDataService_Service factory = new IDataService_Service();
IDataService service = factory.getDataSharedWebService();
long timeout = 30 * 60 * 1000;// 30分钟
// 设置超时时间
// 当前起作用的配置
((BindingProvider) service).getRequestContext().put("javax.xml.ws.client.connectionTimeout", timeout);
((BindingProvider) service).getRequestContext().put("javax.xml.ws.client.receiveTimeout", timeout);
// 网上搜到的其他设置超时的方法,当前项目没有起作用
((BindingProvider) service).getRequestContext().put("com.sun.xml.ws.connect.timeout", timeout);
((BindingProvider) service).getRequestContext().put("com.sun.xml.ws.request.timeout", timeout);
((BindingProvider) service).getRequestContext().put("com.sun.xml.internal.ws.connect.timeout", timeout);
((BindingProvider) service).getRequestContext().put("com.sun.xml.internal.ws.request.timeout", timeout);
((BindingProvider) service).getRequestContext().put("sun.net.client.defaultConnectTimeout", timeout);
((BindingProvider) service).getRequestContext().put("sun.net.client.defaultReadTimeout", timeout);
try {
// 调用WebService
return service.getData(requestData);
} catch (Exception e) {
log.error(e.getMessage());
}
return null;
}
也可以用cxf的wsdl2java命令构建客户端,调用方式一样,但是如何设置超时时间没有试。
方法四:使用HttpClient调用WebService
使用commons-httpclient-3.1,依赖jar包(commons-codec.jar、commons-logging.jar)。
public String call(String wsdl, String requestData) {
try {
// requestData可以直接用soapui中请求的数据,注意的使用
PostMethod postMethod = new PostMethod(wsdl);
byte[] b = requestData.getBytes("UTF-8");
InputStream in = new ByteArrayInputStream(b, 0, b.length);
RequestEntity re = new InputStreamRequestEntity(in,
"text/xml; charset=utf-8");
postMethod.setRequestEntity(re);
// 设置header SOAPAction,不设置的话,会报异常:no SOAPAction
// header,但是SOAPAction的好像任意值都可以
String soapAction = "XX";
postMethod.setRequestHeader("SOAPAction", soapAction);
HttpClient client = new HttpClient();
// 设置超时(不知道默认是多久,没有设置的时候,也没有报错,设置下保险些)
int timeout = 10 * 60 * 1000;
client.getHttpConnectionManager().getParams().setConnectionTimeout(timeout);
client.getHttpConnectionManager().getParams().setSoTimeout(timeout);
//
int status = client.executeMethod(postMethod);
if (status == 200) {// 成功
InputStream is = postMethod.getResponseBodyAsStream();
/**
* 获取的结果可以参考用soapui调用时的返回值,
* 如果约定的返回值是XML,并不会像soapui一样把xml用包含起来,要注意解析的方法,
* 不知道soapui如何处理的,暂时没时间研究。
*/
return getResponseXML(is);
} else {
log.error("调用Webservice出错;错误代码为:" + status);
}
} catch (Exception e) {
log.error(e.getMessage());
}
return null;
}
public String getResponseXML(InputStream response) throws Exception {
// 根据具体的返回值写的解析
SAXReader reader = new SAXReader();
Document document = reader.read(response);
Element root = document.getRootElement();
List childElements = root.elements();
for (Element child : childElements) {
List datas = child.selectNodes("getOrgInfoResponse/getOrgInfoResponse");
for (Element node : datas) {
// 返回约定的xml
return node.getText();
}
}
return null;
}
总结下这次遇到的问题:
1.异常:
java.lang.NoSuchFieldError: QUALIFIED
jar包冲突,删除jar包(如果可以的话,不能删除就用HttpClient的方式)。由于项目中第三方工具需要XmlSchema-1.3.x.jar,而cxf-2.5作为客户端时需要用到xmlschema-core-2.0.1.jar导致前三种方式都不能用。
2.超时
由于接收数据较大,时间较长,必须设置超时时间。
3.异常:
javax.xml.bind.UnmarshalException:unexpected element (uri:"", local:"getDataResponse").Expected elements are <{http://xxx/xx}getOrgInfoResponse>
是namespace的问题,如果用JaxWsProxyFactoryBean的方式,要修改@WebResult中targetNamespace;如果用wsimport命令的方式,需要修改类GetDataResponse中属性getDataResponse的注解中namespace的值。
4.异常:
no SOAPAction header
没有设置SOAPAction,设置方式:postMethod.setRequestHeader("SOAPAction", soapAction),soapAction的值是什么好像并不影响结果。