《Apache CXF开发Web Service 理解CXF Frontends之Code-First》一文提到Code-First和Contract-First两种不同的方式,这里将介绍Contract-First的使用。如果使用Contract-First的开发方式,开发者首先准备好WSDL文档,通过CXF提供的工具wsdl2java来生成代码。这个工具在%CXF-HOME%/bin目录中。Contract-First需要完成的工作有:
生成服务组件
实现服务方法
发布Web Service
开发客户端
使用《Apache CXF开发Web Service 理解CXF Frontends之Code-First》中生成的WSDL文档,即启动mvn test –Pserve后,访问http://localhost:8080/OrderProcess?wsdl所看到的内容。现在是要把操作倒过去,通过这个WSDL来生成SEI。
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="OrderProcessService" targetNamespace="http://order.demo/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://order.demo/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://order.demo/" xmlns:tns="http://order.demo/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="processOrder" type="tns:processOrder" />
<xs:element name="processOrderResponse" type="tns:processOrderResponse" />
<xs:complexType name="processOrder">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="tns:order" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="order">
<xs:sequence>
<xs:element minOccurs="0" name="customerID" type="xs:string" />
<xs:element minOccurs="0" name="itemID" type="xs:string" />
<xs:element name="price" type="xs:double" />
<xs:element name="qty" type="xs:int" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="processOrderResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="processOrderResponse">
<wsdl:part element="tns:processOrderResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="processOrder">
<wsdl:part element="tns:processOrder" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="OrderProcess">
<wsdl:operation name="processOrder">
<wsdl:input message="tns:processOrder" name="processOrder">
</wsdl:input>
<wsdl:output message="tns:processOrderResponse" name="processOrderResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="OrderProcessServiceSoapBinding" type="tns:OrderProcess">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="processOrder">
<soap:operation soapAction="" style="document" />
<wsdl:input name="processOrder">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="processOrderResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="OrderProcessService">
<wsdl:port binding="tns:OrderProcessServiceSoapBinding" name="OrderProcessPort">
<soap:address location="http://localhost:8080/OrderProcess" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
生成服务组件
将准备好的WSDL文档命名为OrderProcess.wsdl。文档的内容与使用JAX-WS规范的JAVA组件对应。
WSDL element |
Java component |
targetNamespace attribute of the <wsdl:definitions> element targetNamespace="http://order.demo/" |
Java package(demo.order) |
<wsdl:portType> <wsdl:portType name="OrderProcess"> |
Java Service Endpoint Interface (SEI) OrderProcess |
<wsdl:operation> child element of the <wsdl:portType> element <wsdl:operation name="processOrder"> |
Java methods processOrder |
<wsdl:service> <wsdl:service name="OrderProcessService"> |
Service class OrderProcessService |
<wsdl:message> |
Service operation parameters |
关于wsdl2java工具的使用请参考:
http://cxf.apache.org/docs/wsdl-to-java.html
http://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html
这里将介绍Maven 的插件 cxf-codegen-plugin 来生成代码的方式。《Apache CXF开发Web Service 理解CXF Frontends之Code-First》文中提到的pom.xml添加cxf-codegen-plugin。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dcb.cxfbook.ch03</groupId>
<artifactId>contractfirst</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>contractfirst</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cxf.version>2.2.3</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
<build>
<finalName>contractfirst</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>src/main/resources/OrderProcess.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>server</id>
<build>
<defaultGoal>test</defaultGoal>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>demo.order.OrderProcessServer</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>client</id>
<build>
<defaultGoal>test</defaultGoal>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>demo.order.client.Client</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
执行命令:
cd contractfirst
mvn generate-sources
Maven cxf-codegen-plugin会为你自动调用CXF 中wsdl2java工具。可能初学Maven的朋友不大理解,这里简单介绍cxf-codegen-plugin的使用。在build.plugins.plugin中添加cxf-codegen-plugin。<wsdl>src/main/resources/OrderProcess.wsdl</wsdl>指明要转换的WSDL文档。<phase>generate-sources</phase>指明执行的命令。<goal>wsdl2java</goal>指明执行命令所包含的工作。
对于Goal和Phase的关系,在《Maven中的几个重要概念(二):lifecycle, phase and goal》一文中有详细的介绍。这里摘选Goal和Phase的内容:
Maven定义了一系列Best Practice,将关联的Phase组合在一起,即执行一个phase会执行某个liefcycle所有的phase。Goal是独立的,可以绑定到多个phase中,也可以不绑定。从这方面讲,phase就是goal的容器,实际被执行的是goal。
在本文中执行generate-sources这个phase,其实是执行cxf-codegen-plugin中的<goal>wsdl2java</goal>
对于Maven的生命周期,请看《Maven生命周期详解》。
跑题了半天,让我们看看这mvn generate-sources到底生成了什么内容:
JAXB输入/输出消息类。wsdl2java类会分别生成JAVA输入/输出消息组件。本例中将会生成ProcessOrder作为输入类,生成ProcessOrderResponse作为输出类,还会根据<xs:complexType name="order">生成Order类。
服务接口。本例中服务接口为OrderProcess。
服务实现类。本例中提供了继承自Service接口的实现类OrderProcessService。我们可以修改这个类来实现功能。
这些类都在包demo.order中。wsdl2java工具从targetNamespace="http://order.demo/"生成包名,也就是http://order.demo/除去http://后倒写。
如果修改pom.xml,添加如下内容,将会生成OrderProcessImpl.java(示例的实现服务类)和OrderProcess_OrderProcessPort_Server.java(CXF提供的独立的服务器)。
<wsdlOption>
<wsdl>src/main/resources/OrderProcess.wsdl</wsdl>
<extraargs>
<extraarg>-server</extraarg>
<extraarg>-impl</extraarg>
<extraarg>-verbose</extraarg>
</extraargs>
</wsdlOption>
JAXB 输入/输出消息类
ProcessOrder, ProcessOrderResponse和Order这三个类代表Web Service操作类。这几个类有着各种JAXB注解。ProcessOrder和 ProcessOrderResponse用来表示Request和Response。Request拥有出入参数引用,而Response着拥有输出参数引用。
为了理解Request和Response的概念,来看看Web Service将会提交什么样的SOAP Request消息。
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:processOrder xmlns:ns2="http://order.demo/">
<arg0>
<customerID>C001</customerID>
<itemID>I001</itemID>
<price>200.0</price>
<qty>100</qty>
</arg0>
</ns2:processOrder>
</soap:Body>
</soap:Envelope>
processOrder映射为Web Service中的方法processOrder。子元素arg0代表着SOAP payload,映射为输入参数Order。CXF会自动将arg0转换为Order对象,并执行processOrder方法。
服务接口
OrderProcess是SEI,定义了processOrder方法。
@WebService(targetNamespace = "http://order.demo/", name = "OrderProcess")
@XmlSeeAlso({ObjectFactory.class})
public interface OrderProcess {
@WebResult(name = "return", targetNamespace = "")
@RequestWrapper(localName = "processOrder", targetNamespace = "http://order.demo/", className = "demo.order.ProcessOrder")
@ResponseWrapper(localName = "processOrderResponse", targetNamespace = "http://order.demo/", className = "demo.order.ProcessOrderResponse")
@WebMethod
public java.lang.String processOrder(
@WebParam(name = "arg0", targetNamespace = "")
demo.order.Order arg0
);
}
@WebService定义了这个接口是SEI。
@Xml SeeAlso通知JAXB在执行数据绑定是包含ObjectFactory。
@RequestWrapper包含了输入消息。
@ResponseWrapper包含了输出消息。
@WebResult指明返回的类型。
@WebMethod指明这是个服务方法。
@WebParam指明参数。
运行Web Service
《Apache CXF开发Web Service 理解CXF Frontends之Code-First》一文执行过程类似。
cd contractfirst
#启动server,显示Server ready...消息
mvn test –Pserver
#执行client,显示The order ID is ORD1234
mvn test -Pclient
参考内容
《Maven中的几个重要概念(二):lifecycle, phase and goal》
http://cxf.apache.org/docs/wsdl-to-java.html
http://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html