教程(2)依据教程(1)中提供的WSDL契约,对其发布的webService创建了一个简单的客户端;本文详细介绍一下webService客户端开发的一般步骤。
在CXF中,开发一个service消费者(或客户端)的起点是一个WSDL契约,连同端口类型、绑定以及service定义。然后我们就可以使用 wsdl2java 工具来根据WSDL契约生成Java stub 代码。stub代码提供了调用远端服务方法所需的支持代码。
wsdl2java 工具可以为CXF客户端生成下列代码:
下面是HelloWorld WSDL 契约的示例。该契约定义了一个单一的端口类型,Greeter;一个SOAP绑定,Greater_SOAPBinding;以及一个服务,SOAPService,它有一个单独的端口,SoapPort。
"1.0" encoding="UTF-8"?>
"HelloWorld" targetNamespace="http://apache.org/hello_world_soap_http"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://apache.org/hello_world_soap_http"
xmlns:x1="http://apache.org/hello_world_soap_http/types"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
"http://apache.org/hello_world_soap_http/types"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://apache.org/hello_world_soap_http/types"
elementFormDefault="qualified">
"MyStringType">
"string">
"30" />
"sayHi">
"sayHiResponse">
"responseType" type="string"/>
"greetMe">
"requestType" type="tns:MyStringType"/>
"greetMeResponse">
"responseType" type="string"/>
"greetMeOneWay">
"requestType" type="string"/>
"pingMe">
"pingMeResponse">
"faultDetail">
"minor" type="short"/>
"major" type="short"/>
"sayHiRequest">
"x1:sayHi" name="in"/>
"sayHiResponse">
"x1:sayHiResponse" name="out"/>
"greetMeRequest">
"x1:greetMe" name="in"/>
"greetMeResponse">
"x1:greetMeResponse" name="out"/>
"greetMeOneWayRequest">
"x1:greetMeOneWay" name="in"/>
"pingMeRequest">
"in" element="x1:pingMe"/>
"pingMeResponse">
"out" element="x1:pingMeResponse"/>
"pingMeFault">
"faultDetail" element="x1:faultDetail"/>
"Greeter">
"sayHi">
"tns:sayHiRequest" name="sayHiRequest"/>
"tns:sayHiResponse" name="sayHiResponse"/>
"greetMe">
"tns:greetMeRequest" name="greetMeRequest"/>
"tns:greetMeResponse" name="greetMeResponse"/>
"greetMeOneWay">
"tns:greetMeOneWayRequest"
name="greetMeOneWayRequest"/>
"pingMe">
"pingMeRequest" message="tns:pingMeRequest"/>
"pingMeResponse" message="tns:pingMeResponse"/>
"pingMeFault" message="tns:pingMeFault"/>
"Greeter_SOAPBinding" type="tns:Greeter">
"document"
transport="http://schemas.xmlsoap.org/soap/http"/>
"sayHi">
"" style="document"/>
"sayHiRequest">
"literal"/>
"sayHiResponse">
"literal"/>
"greetMe">
"" style="document"/>
"greetMeRequest">
"literal"/>
"greetMeResponse">
"literal"/>
"greetMeOneWay">
"" style="document"/>
"greetMeOneWayRequest">
"literal"/>
"pingMe">
"document"/>
"literal"/>
"literal"/>
"pingMeFault">
"pingMeFault" use="literal"/>
"SOAPService">
"tns:Greeter_SOAPBinding" name="SoapPort">
"http://localhost:9000/SoapContext/SoapPort"/>
Greeter 端口类型定义了下列WSDL 操作:
该WSDL还为SOAP协议定义了一个绑定。实际上,这个绑定通常是自动生成的 —— 例如,通过执行CXF wsdl2soap 或 wsdl2xml 工具。同样,SOAPService 可以通过执行CXF的wsdl2service工具自动生成。
有了WSDL契约之后,我们可以用CXF的wsdl2java工具来生成客户端代码。在命令行提示符下输入类似下面这样的命令:
wsdl2java -ant -client -d D:/temp -p com.neareast.test.cxf.client.WSDL2Java -frontend jaxws21 hello_world.wsdl
最后的 hello_world.wsdl 是一个包含上述WSDL契约的文件,(也可以指定一个服务的URL地址)。其他参数均为常用的可选参数,各参数的作用为:
如果没有用-p参数指定包名,上面的命令会生成下面两个包:
wsdl2java 命令生成的 stub 文件分成下列类型:
这一章节描述了如何基于上述的WSDL契约,来写一个简单的Java客户端。要实现客户端,我们需要使用以下 stub 类:
下面是生成名为的服务类的一个典型的轮廓,我们暂且叫它ServiceName类,可以看到它扩展了javax.xml.ws.Service 基类。
public class ServiceName extends javax.xml.ws.Service
{
...
public ServiceName(URL wsdlLocation, QName serviceName) { }
public ServiceName() { }
public Greeter getPortName() { }
.
.
.
}
ServiceName 类定义了如下方法:
对于原始的WSDL契约中定义的每一个端口类型,我们都可以生成对应的服务端点接口Java代码。一个服务端点接口就是一个WSDL端口类型的Java映射。原始WSDL端口类型中定义的每一个操作都映射为服务端点接口中对应的一个方法。操作的参数的映射规则如下:
例如,下面展示的是Greeter服务端点接口,它是由前面的WSDL契约中定义的Greeter端口类型生成的。为简单起见,下面的例子省略了标准的JAXB 及 JAX-WS 注解。
/* Generated by WSDLToJava Compiler. */
package org.objectweb.hello_world_soap_http;
...
public interface Greeter
{
public java.lang.String sayHi();
public java.lang.String greetMe(java.lang.String requestType);
public void greetMeOneWay(java.lang.String requestType);
public void pingMe() throws PingMeFault;
}
下面是实现了简单客户端的Java代码。简要地说,客户端连接到SOAPService 服务的SoapPort 端口,然后调用Greeter 端口类型支持的每一个操作。
package demo.hw.client;
import java.io.File;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.hello_world_soap_http.Greeter;
import org.apache.hello_world_soap_http.PingMeFault;
import org.apche.hello_world_soap_http.SOAPService;
public final class Client {
private static final QName SERVICE_NAME =
new QName("http://apache.org/hello_world_soap_http",
"SOAPService");
private Client()
{
}
public static void main(String args[]) throws Exception
{
if (args.length == 0)
{
System.out.println("please specify wsdl");
System.exit(1);
}
URL wsdlURL;
File wsdlFile = new File(args[0]);
if (wsdlFile.exists())
{
wsdlURL = wsdlFile.toURL();
}
else
{
wsdlURL = new URL(args[0]);
}
System.out.println(wsdlURL);
SOAPService ss = new SOAPService(wsdlURL, SERVICE_NAME);
Greeter port = ss.getSoapPort();
String resp;
System.out.println("Invoking sayHi...");
resp = port.sayHi();
System.out.println("Server responded with: " + resp);
System.out.println();
System.out.println("Invoking greetMe...");
resp = port.greetMe(System.getProperty("user.name"));
System.out.println("Server responded with: " + resp);
System.out.println();
System.out.println("Invoking greetMeOneWay...");
port.greetMeOneWay(System.getProperty("user.name"));
System.out.println("No response from server as method is OneWay");
System.out.println();
try {
System.out.println("Invoking pingMe, expecting exception...");
port.pingMe();
} catch (PingMeFault ex) {
System.out.println("Expected exception: PingMeFault has occurred.");
System.out.println(ex.toString());
}
System.exit(0);
}
}
Client.main()函数的执行过程如下:
SOAPService ss = new SOAPService(wsdlURL, SERVICE_NAME);
Greeter port = ss.getSoapPort();
要创建一个新的端口对象,我们先创建一个服务对象(传入WSDL地址以及服务名这两个参数),然后调用合适的getPortName() 方法来获取我们需要的特定的端口的实例。本例中,SOAPService 服务仅支持SoapPort端口,也就是 Greeter类型。
参考文档:http://cxf.apache.org/docs/developing-a-consumer.html