这篇笔记,只是针对于基于wsdl文件生成客户端代码后,来编写访问webservice的客户端。作为开发过程中遇到的问题的记录。
开发环境:jdk1.5、xfire-1.2.6
由于服务端并非本公司开发,所以只能拿到服务端的wsdl文件,首先想到的是,基于wsdl文件生成客户端代码,然后在此基础上按照业务来编写请求类。
XFire 官方网站 http://xfire.codehaus.org
可以从该网站下载xfire-distribution-1.2.6.zip文件,里面包含了xFire的文档、类包及相关文档说明。
jar包都在lib下,另外不要忘记了xfire-all-1.2.6.jar包。
一、new一个project:MyWebservice
然后右键再new一下,选择other:
上图中的WSDL URL or path栏可以选择url地址:http://<ip>:<port>/path/XXX?wsdl,也可以将wsdl文件下载到本地,选择本地wsdl文件。由于本项目用到的url本地无法访问,因此必须在服务器上下载服务端的wsdl文件到本地,利用本地的wsdl文件生成客户端。
Output directory一栏,是选择你生成的代码放入的目录。由于之前新建了一个MyWebservice的工程,所以这里选择该工程的src目录,当然也可以新建你指定的目录列表(下面的package一栏可以选择指定的目录,这里暂时选择default)。如下图:
点击finish完成,生成如下图目录结构的客户端代码(MyClient类是后面自己编写的访问请求类)。
二、编写客户端调用类:MyClient
package com.telement.intf.huaxia.service.impl; import java.net.MalformedURLException; import java.net.URL; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import org.codehaus.xfire.XFireFactory; import org.codehaus.xfire.client.Client; import org.codehaus.xfire.client.XFireProxyFactory; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.service.binding.ObjectServiceFactory; public class MyClient { public static void main(String[] args) { MyClient mc = new MyClient(); mc.queryWebSiteByXfireThree(); } public void queryWebSiteByXfireOne() { Service srvcModel = new ObjectServiceFactory() .create(HuaXiaServiceImplPortType.class); XFireProxyFactory factory = new XFireProxyFactory(XFireFactory .newInstance().getXFire()); String helloWorldURL = "http://<ip>:<port>/path/serviceName"; String requestXmlStr = "yours_request_str"; try { HuaXiaServiceImplPortType srvc = (HuaXiaServiceImplPortType) factory .create(srvcModel, helloWorldURL); JAXBElement<String> requestParm = new JAXBElement<String>( new QName("http://impl.service.huaxia.intf.telement.com/", "HuaXiaServiceImplPort"), String.class, requestXmlStr); JAXBElement<String> resultStr = srvc.queryWebSite(requestParm); System.out.println(resultStr.getValue()); } catch (MalformedURLException e) { e.printStackTrace(); } } public void queryWebSiteByXfireTwo() { String requestXmlStr = "yours_request_str"; URL url = null; Client client = null; try { url = new URL("http://<ip>:<port>/path/serviceName?wsdl"); client = new Client(url); System.out.println(requestXmlStr); String result = (String) client.invoke("queryWebSite", new Object[] { requestXmlStr })[0]; System.out.println(result); } catch (MalformedURLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public void queryWebSiteByXfireThree() { String requestXmlStr = "yours_request_str"; HuaXiaServiceImplClient hxc = new HuaXiaServiceImplClient(); HuaXiaServiceImplPortType service = hxc.getHuaXiaServiceImplPort(); JAXBElement<String> requestParm = new JAXBElement<String>(new QName( "http://impl.service.huaxia.intf.telement.com/", "HuaXiaServiceImplPort"), String.class, requestXmlStr); JAXBElement<String> resultStr = service.queryWebSite(requestParm); System.out.println(resultStr.getValue()); } }
调用上述类中方法一和方法三测试后发现如下异常:(方法二正常,因为方法二与生成的客户端存根代码无关)
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null org.codehaus.xfire.fault.XFireFault: Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null at org.codehaus.xfire.fault.XFireFault.createFault(XFireFault.java:89) at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:83) at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114) at org.codehaus.xfire.client.Client.invoke(Client.java:336) at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77) at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57) at $Proxy6.queryWebSite(Unknown Source) at com.telement.intf.huaxia.service.impl.MyClient.queryWebSiteByXfireOne(MyClient.java:77) at com.telement.intf.huaxia.service.impl.MyClient.main(MyClient.java:27) Caused by: org.codehaus.xfire.XFireRuntimeException: Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null at org.codehaus.xfire.aegis.type.basic.BeanType.readProperty(BeanType.java:446) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:377) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeObject(ArrayType.java:210) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeObject(ArrayType.java:210) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeObject(ArrayType.java:210) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392) at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298)
思路一直停留在org.codehaus.xfire.XFireRuntimeException: Could not invoke service上面,但后面发现修改
wsdl文件中的一个属性nillable,可以解决下面的这个Exception:
Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null
从服务端下载到的wsdl文件中,<xsd:element>元素存在这么几个元素:
<xsd:element minOccurs="0" name="requestXml" nillable="true" type="xsd:string"></xsd:element>
造成在生成客户端代码的时候将本来是String类型的请求参数和返回值都封装成了JAXBElement<String>这个类,因此在编写MyClient这个类的时候,自然就想到要将请求参数封装为JAXBElement<String>。
经过调试发现改为:
<xsd:element minOccurs="0" name="requestXml" nillable="false" type="xsd:string"></xsd:element>
或者
<xsd:element maxOccurs="1" minOccurs="1" name="requestXml" nillable="true" type="xsd:string"></xsd:element>
再根据wsdl文件来生成客户端,便是本真的String类型作为参数和返回的数据类型。
三、修改编写好的客户端调用类:MyClient
package com.telement.intf.huaxia.service.impl; import java.net.MalformedURLException; import java.net.URL; import org.codehaus.xfire.XFireFactory; import org.codehaus.xfire.client.Client; import org.codehaus.xfire.client.XFireProxyFactory; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.service.binding.ObjectServiceFactory; public class MyClient { public static void main(String[] args) { MyClient mc = new MyClient(); mc.queryWebSiteByXfireOne(); } public void queryWebSiteByXfireOne() { Service srvcModel = new ObjectServiceFactory() .create(HuaXiaServiceImplPortType.class); XFireProxyFactory factory = new XFireProxyFactory(XFireFactory .newInstance().getXFire()); String helloWorldURL = "http://<ip>:<port>/path/serviceName"; String requestXmlStr = "yours_request_str"; try { HuaXiaServiceImplPortType srvc = (HuaXiaServiceImplPortType) factory .create(srvcModel, helloWorldURL); String resultStr = srvc.queryWebSite(requestXmlStr); System.out.println(resultStr); } catch (MalformedURLException e) { e.printStackTrace(); } } public void queryWebSiteByXfireTwo() { String requestXmlStr = "yours_request_str"; URL url = null; Client client = null; try { url = new URL("http://<ip>:<port>/path/serviceName?wsdl"); client = new Client(url); System.out.println(requestXmlStr); String result = (String) client.invoke("queryWebSite", new Object[] { requestXmlStr })[0]; System.out.println(result); } catch (MalformedURLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public void queryWebSiteByXfireThree() { String requestXmlStr = "yours_request_str"; HuaXiaServiceImplClient hxc = new HuaXiaServiceImplClient(); HuaXiaServiceImplPortType service = hxc.getHuaXiaServiceImplPort(); String resultStr = service.queryWebSite(requestXmlStr); System.out.println(resultStr); } }
方法一的方式调用依然存在Exception:
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: Fault occurred while proce ssing. org.codehaus.xfire.fault.XFireFault: Fault occurred while processing. at org.codehaus.xfire.fault.Soap11FaultSerializer.readMessage(Soap11FaultSerializer.java:31) at org.codehaus.xfire.fault.SoapFaultSerializer.readMessage(SoapFaultSerializer.java:28) at org.codehaus.xfire.soap.handler.ReadHeadersHandler.checkForFault(ReadHeadersHandler.java:111) at org.codehaus.xfire.soap.handler.ReadHeadersHandler.invoke(ReadHeadersHandler.java:67) at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131) at org.codehaus.xfire.client.Client.onReceive(Client.java:406) at org.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:139) at org.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48) at org.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26) at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131) at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79) at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114) at org.codehaus.xfire.client.Client.invoke(Client.java:336) at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77) at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57) at $Proxy6.queryWebSite(Unknown Source) at com.telement.intf.huaxia.service.impl.MyClient.queryWebSiteByXfireOne(MyClient.java:31) at com.telement.intf.huaxia.service.impl.MyClient.main(MyClient.java:16)
但方法二和方法三调用可正常返回结果。
由于在网上搜索到很多文章都是提到xfire客户端调用方式是上述MyClient类中的方法一,所以笔者写了一个简单的服务端,然后另外起一个project来生成客户端并测试,发现确实是行得通的。所以暂时将该问题记录下来,如果有知道原因的童鞋,也请指点下。