Web 服务描述语言(Web Services Description Language,WSDL)是一种基于 XML 的格式,它正在成为一种业界标准,用于以与实现无关的方式描述 Web 服务。将创建和使用 Web 服务的许多不同的方法结合起来,是一种常见的思路。本教程将让您牢固掌握 WSDL 的用途和机制,以及在使 Web 服务变得可互操作的工作中,它如何起到至关重要的作用。
本教程包括了几个实验,它们使用 IBM WebSphere SDK for Web Services V5(WSDK)仅从一个 WSDL 文件开始创建 Web 服务和客户机。我们将向您说明 WSDK 中可用的高级工具如何能帮助您在使用 WSDL 时提高 Web 服务的开发速度。
学习本教程所必备的知识
在本教程中,假设读者已经顺利学完了教程:“Web 服务和 WSDK 简介(Introduction to Web services and the WSDK)”和“通过 Java 类创建 Web 服务(Creating a Web service from a Java class)”,或者读者拥有同等水平的 Web 服务和 WSDK 知识。这意味着,您具备了 Web 服务的应用知识,并且可以使用 IBM WSDK 执行基本的操作,譬如启动和停止服务器以及部署 Web 服务。本教程还假定您已具备了 Java 编程语言和 XML 的应用知识。J2EE 和 Ant 的知识会有所帮助,但不是必需的。
所有的示例应用程序将部署在同 WSDK 一起提供的嵌入式 IBM WebSphere Application Server 上。提供的 Ant 构建脚本可以使构建和部署这些示例应用程序更方便和更可靠。在本教程结尾处的参考资料一章中,提供了有关 Ant、Java 技术、XML、IBM WebSphere 和 J2EE 方面的入门参考材料的链接。
本教程包括的内容
本教程介绍了 WSDL 和 WSDK 所提供的工具,这些工具使用 WSDL 来开发基于 Web 服务的系统。其中将包含以下主题、工具和技术:
主题
- WSDL 回顾。
- WSDL 简介。
- WSDL 的 <types>、<message>、<portType>、<binding> 和 <service> 元素。
- XML Schema 概述。
- 消息传递和编码样式。
工具
- WSDL2WebService。
技术
- 通过 WSDL 文件创建 Web 服务实现。
- 通过 WSDL 文件创建 Web 服务客户机。
本教程中您所需的工具
最起码您需要一个简单的文本编辑器和一个 Java SDK 1.3.1(WSDK 中包含了它)或者更高版本,用于编译示例。您还将需要 IBM WebSphere SDK for Web Services(WSDK)。可以下载 IBM WSDK 并获取有关它的信息。您也可以索取免费的 CD,这些 CD 中包含 WSDK 和其它 IBM Web 服务现成产品(譬如来自 developerWorks Speed-start Web services 计划的 WebSphere Studio)。WSDK 提供了它自己的 Java SDK 和一些用于启动、停止和管理与 WSDK 一同提供的嵌入式 WebSphere Application Server 的批处理文件和程序。
虽然以手工方式构建和部署应用程序可能会有趣些,但教程中的所有示例都包含了 Ant 构建脚本,可以用这些脚本来构建和部署我们将创建的 Web 服务。在 Apache 的 Ant 主页上可以找到 Ant。
因为有许多 JAR 文件要管理,所以建议您使用集成开发环境(Interactive Development Environment,IDE)。我们使用 Eclipse 来构建样本应用程序,Eclipse 是一个免费使用的 IDE。虽然 Eclipse 和 WebSphere Studio Application Developer(Application Developer)都不是必需的,但可以分别在 Eclipse 网站和 IBM WebSphere 开发人员专区上找到它们。
WSDL 简介
简介
Web 服务的引入已经在许多技术圈中引起了一阵兴奋。这种兴奋的缘由有许多;其中有两个原因便是对互操作性和更快的上市时间的承诺。互操作性部分是通过使用常见的开放协议(比如 HTTP 和 SOAP)实现的。但是,这还不足以使服务器进程的实现对客户机完全透明。
客户机还必须知道 Web 服务的数据类型、参数、返回类型、位置和传输细节。还需要元协议,该协议可以用不特定于供应商和实现的方式描述这些基本要素,这样客户机和服务器就可能完全分离了。
Web 服务描述语言(WSDL)是基于 XML 的文件格式,它不但描述了这些 Web 服务接口的细节,而且描述了抽象接口是如何与给定的传输协议(HTTP 和 SMTP 等等)和编码(SOAP 等等)结合在一起的。但是 WSDL 的优点远不止这些来自互操作性的优点。WSDL 是一个 W3C 纪要(W3C Note)。通过绑定到规范,供应商不但能够创建使用 WSDL 生成客户机运行时代码(这些代码与 Web 服务进行交互)的工具,而且还可以创建使用 WSDL 生成服务器端模板代码的工具。
而 WSDL 不但有助于完全不同的进程进行互操作,还有助于缩短开发支持 Web 服务的客户机和服务器所需的时间。WSDK 附带了WSDL2WebService
工具,该工具使用 WSDL,通过生成用于 Web 服务的客户机运行时代码和服务器模板代码来提供帮助,使开发人员的项目能够启动并运行。
我们将更详细地讨论 WSDL2WebService
工具,在此之前首先研究一下 WSDL 的体系结构。
WSDL 体系结构
WSDL 提供了一种语法,用于将服务描述成一组交换消息的端点。WSDL 文档充当一个或多个服务的(XML)描述,这种描述是与语言和平台无关的。WSDL 文档描述了这些服务、如何访问它们以及期望的响应类型(如果有的话)。WSDL 描述了服务的类型、消息、操作、portType、位置和协议绑定。您使用 WSDL 将 Web 服务描述成一组对消息进行操作的端点。WSDL 可以描述面向文档的信息,也可以描述面向过程的信息。在介绍特定于 WSDL 的元素之前,让我们先介绍 WSDL 试图描述的一些概念。
portType:portType
描述 Web 服务所提供的操作。它类似于一个 Java 接口,因为它描述了一组操作。它将消息元素合并成操作。
message 和 types:message
是一个数据元素。操作用它来传送该操作的数据。消息通过列出所交换的数据类型来描述客户机和服务之间的通信。types
元素中描述了类型,通常用 XML Schema 完成描述。types 类似于 Java 类和基本类型。
operation、message 和 fault:operation
就像一个 Java 方法。它包含了传入消息、传出消息和出错消息。您可以将操作的传入消息当作是 Java 编程语言中方法的参数。可以将操作的传出消息当作 Java 编程语言中方法的返回类型。可以将出错消息当作 Java 异常。
binding:binding 将 portType 绑定到特定的协议(例如 SOAP 1.1、HTTP GET/POST 或 MIME)。
service:service 定义了某个特定绑定(binding)的连接信息。服务可以有一个或多个端口,每个端口都定义一个不同的连接方法(例如 HTTP / SMTP 等等)。
总而言之,一个 WSDL 协议实例描述五项内容:
- types,Web 服务接口的数据类型(其参数和返回类型)。
- message,将数据类型变量分组以进行网络传输。
- portType,将消息分组成逻辑操作。
- binding,描述如何将 portType 映射成传输/消息传递协议。
- service,列出某个特定绑定的连接信息。
在接下来的几页中会用示例更加详细地描述上面这些项中的每一项。
<types> 元素
<types> WSDL 元素允许您指定数据类型,无论这些类型多简单或多复杂,这些类型是 WSDL 文件所描述的 Web 服务接口必需的。WSDL 纪要对于定义类型所采用的协议没有提出任何要求,尽管它支持将 XML Schema 用于其规范的类型协议。XML Schema 很常见,而且是开放的,因此在 WSDL 中使用它使得 Web 服务定义不受到特定于编程语言的类型的约束,也不受到特定于供应商的类型的约束。
下面这个示例的 <types> 元素使用 XML Schema 来定义七种数据类型。正如您所看到的,XML Schema 非常灵活,允许您用一种开放和常见的方式定义具体针对您的问题领域的类型。现在可以在 WSDL 中引用这些类型来进一步描述 Web 服务接口。
<wsdl:definitions
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://dvd.com"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://dvd.com">
...
<wsdl:types>
<xsd:schema>
<xsd:simpleType name="EmailAddressType">
<xsd:restriction base="xsd:string">
<xsd:pattern value=".+@.+"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="PriceType">
<xsd:restriction base="xsd:decimal">
<xsd:totalDigits value="6"/>
<xsd:fractionDigits value="2"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="SerialNumberType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[0-9a-zA-Z]{3}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{3}" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="DVDAbstractType">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="artist" type="xsd:string"/>
<xsd:element name="releaseDate" type="xsd:date"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DVDPurchaseType">
<xsd:complexContent>
<xsd:extension base="tns:DVDAbstractType">
<xsd:sequence>
<xsd:element name="cost" type="tns:PriceType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="DVDRentalType">
<xsd:complexContent>
<xsd:extension base="tns:DVDAbstractType">
<xsd:sequence>
<xsd:element name="rentalCost" type="tns:PriceType"/>
<xsd:element name="returnDate" type="xsd:date" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ResponseConfirmationType">
<xsd:sequence>
<xsd:element name="confirmationNumber" type="tns:SerialNumberType" />
<xsd:element name="message" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
...
</wsdl:definitions>
下一页大致地描述了 XML Schema 的类型系统。
XML Schema
Web 服务全是关于进程间的通信;它可使完全不同的计算机进行通信。将 WSDL 和 WSDK 联合起来使用,使您能够更轻松地将 Web 服务集成到目前的 IT 基础设施中,因为 WSDK 的 WSDL2WebService
工具可以为开发人员生成客户机运行时代码(如果需要的话)和服务器代码。
当您研究 Web 服务的互操作特征时,类型问题就出现了。例如,如果一台服务器(用某种语言/技术编写而成)想要把一个 numeric 发送给一台客户机(用另一种语言/技术编写而成),那么该服务器该如何告诉客户机已发送数据类型的含义呢?这两个进程对“numeric”的含义可能有不同的定义。客户机如何得知它读入的那几个字节的一串字符不能被解释成字符串或序列化对象,而要解析成一个数字呢?当您研究一个复杂类型(例如 DVD 和 Address 等等)时这个问题就更加明显了。在复杂类型中不仅需要定义所包含的简单类型,还需要定义包含这些简单类型的类型。
XML Schema 为 Web 服务提供了一种获得业界认可的方式,用来定义复杂数据类型以及预先确定好的简单数据类型(例如 string、date 和 numeric 等等)。遵循 XML Schema 规则的用户定义的 XML 协议的描述通常位于 WSDL 文件中。
让我们研究一下一些简单的 XML Schema 示例。
<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
<!-- Other stuff -->
<xsd:element name='name' type='xsd:string'/>
<xsd:element name='age' type='xsd:positiveInteger'/>
</xsd:schema>
在上面的示例中,类型“string”通过前缀“xsd”与名称空间“http://www.w3.org/2001/XMLSchema”相关联。解析器就是通过这种方法知道类型“string”可在 XML Schema 规范中找到的。这些类型很容易理解,下面也列出了这些类型。
复杂类型是简单类型和/或其它复杂类型的特性的命名聚集。例如,研究一下类型 DVD,该类型的特性有:title、artist、releaseDate、listPrice 和 price。DVD 实例的 XML 表示可能类似于这样:
<DVD ID="1232232">
<title>Greatest Hits of the 80s</title>
<artist>To Be AnnouncedVarious</artist>
<releaseDate>1980-01-01</releaseDate>
<listPrice>12.00</listPrice>
<price>3.00</price>
</DVD>
XML Schema 给我们提供了一种描述该 XML 文档所遵循的协议或模式的方式。和基于 DTD 的描述不同的是,XML Schema 是基于 XML 的,几乎是完全可扩展的。上述实例的 XML Schema 文档可能是:
<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
<xsd:element name="DVD">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="title" type="xsd:string" minOccurs="1" maxOccurs="1" />
<xsd:element name="artist" type="xsd:string" minOccurs="1" maxOccurs="1" />
<xsd:element name="releaseDate" xsd:type="date" minOccurs="1" maxOccurs="1" />
<xsd:element name="listPrice" type="xsd:string" minOccurs="1" maxOccurs="1" />
<xsd:element name="price" type="xsd:string" minOccurs="1" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="ID" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
- schema 元素是 XML Schema 文档的根元素。XML 协议的所有元素类型和属性类型的定义都在其中。
- element 元素允许我们定义元素的类型。
-
complexType 元素定义了一个复杂类型。如果一个元素有属性和/或子元素,那么就认为该元素的类型是复杂类型。DVD 类型既有属性又有子元素。元素的子元素可以遵循三种模型:any、choice 和 sequence。
- 类型 any 意味着任何嵌套内容都可以以任何顺序显示成父节点的子节点。
- 类型 choice 意味着只可以使用一组可能的子元素中的一个。
- 最后,sequence 意味着子元素在父元素中的显示顺序必须和它们在 sequence 元素中的顺序相同。
- 在上面的案例中,由于使用了 sequence,因此 DVD 的子元素必须按这样的顺序显示:title、artist、releaseDate、listPrice,然后是 price。attribute 元素指出在该协议中 DVD 元素有一个 ID 属性。
虽然使用复杂类型来验证并记录 SOAP 流所包含的内容,但是 SOAP 流本身并没有包含任何用复杂类型定义的 XML Schema 数据。只有想要在 WSDL 文件中读取或创建自己的类型的时候,才有必要知道如何用 XML Schema 定义复杂类型。
实际上,您不必真正彻底地了解 Web 服务中的类型和类型定义的主题才去编写 Web 服务系统。大多数开发人员所利用的存根/调用 API 和框架/助手代码层负责有关对客户机和服务器之间的数据进行序列化和反序列化的低级细节。这使得整个类型转换过程对于开发人员是透明的。但是,如果您希望读懂 WSDL 文件,或者从头开始编写它们,那么熟知 XML Schema 是必要的。
XML Schema 建议书定义了许多简单的数据类型(请参阅下表)。“简单”指的是标量/名-值对。例如 12334、“hello”、123.322 和 12:12:2005 等等。它们有一个共同点 - 很容易将它们描述成字符串 - 也就是说,它们都没有复杂结构。但是,其中有些确实具有结构。例如日期(date)类型具有把年月日分开的分隔符。简单类型有扩展/限制语法,它允许您使用 XML Schema 简单类型作为基类型来创建/定义您自己的简单类型。请查询 XML Schema 规范以获取更多信息。
string | 任意的字符序列。 |
---|---|
normalizedString | 任何字符序列,其中允许的空白字符只有空格。 |
token | 任何字符序列,其中允许的空白都是单个的空格(也就是说,两个或更多空格连在一起是非法的)。 |
Name | 和 XML 名称相同。 |
QName | 具有名称空间前缀部分、分号和自身名称部分的 XML 名称。 |
NMTOKEN | 和 XML NMTOKEN 相同。 |
NMTOKENS | 和 XML NMTOKEN 相同。 |
byte | -128 到 127 之间的整数(包括 -128 和 127)。 |
unsignedByte | 0 到 255 之间的整数(包括 0 和 255)。 |
base64Binary | 任意长度的用 Base64 编码的数据流。 |
hexBinary | 任意长度的用十六进制编码的数据流。 |
int | -2147483648 到 2147483647 之间的整数(包括 -2147483648 和 2147483647)。 |
unsignedInt | 0 到 4294967295 之间的整数(包括 0 和 4294967295)。 |
integer | 任意整数。 |
positiveInteger | 1 到正无穷大之间的整数(包括 1)。 |
negativeInteger | 负无穷大到 -1 之间的整数(包括 -1)。 |
nonNegativeInteger | 0 到正无穷大之间的整数(包括 0)。 |
nonPositiveInteger | 负无穷大到 0 之间的整数(包括 0)。 |
long | -9223372036854775808 到 9223372036854775807 之间的整数(包括 -9223372036854775808 和 9223372036854775807)。 |
unsignedLong | 0 到 18446744073709551615 之间的整数(包括 0 和 18446744073709551615)。 |
short | -32768 到 32767 之间的整数(包括 -32768 和 32767)。 |
unsignedShort | 0 到 65535 之间的整数(包括 0 和 65535)。 |
decimal | 任意带小数点的数。 |
float | 任何 IEEE 单精度 32 位浮点类型 [IEEE 754-1985] 值。 |
double | 任何 IEEE 单精度 64 位浮点类型 [IEEE 754-1985] 值。 |
boolean | 值为 true 或 false。 |
time | 一天中的某个时刻,格式为 hh:mm:ss.sss。 |
dateTime | 某一时刻,格式为 CCYY-MM-DDThh:mm:ss。 |
duration | PnYnMnDTnHnMnS 格式的时间段,其中“T”是日期/时间的分隔符。允许在前面加上可选的“-”表示已经过去的时期。 |
date | CCYY-MM-DD 格式的某一时刻。 |
anyURI | 任何符合 URI 规则的字符串。 |
<message> 元素
<message> 元素将数据(数据类型在 <types> 元素中进行定义)分组成一个用于逻辑网络传输的特征符,并将数据绑定到一个名称上。该名称用来在操作定义的上下文中引用 <message>。<message> 中的每个数据实例都在 <part> 元素中进行了声明。请参阅下面的图和示例。
<wsdl:definitions
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
...>
...
<wsdl:message name="Subscribe">
<wsdl:part name="email" type="tns:EmailAddressType"/>
<wsdl:part name="until" type="xsd:date"/>
</wsdl:message>
<wsdl:message name="PurchaseResponse">
<wsdl:part name="paymentResponse" type="tns:ResponseConfirmationType" />
<wsdl:part name="purchaseDate" type="xsd:date" />
</wsdl:message>
<wsdl:message name="PurchaseRequest">
<wsdl:part name="purchaseRequest" type="tns:DVDPurchaseType" />
</wsdl:message>
<wsdl:message name="RentalRequest">
<wsdl:part name="rentalRequest" type="tns:DVDRentalType" />
</wsdl:message>
<wsdl:message name="PaymentFault">
<wsdl:part name="errorCode" type="tns:SerialNumberType"/>
</wsdl:message>
...
</wsdl:definitions>
<portType> 元素
<portType> 元素在 WSDL 中相当于 Java 中的接口。它将对 <message> 元素的引用分组成逻辑操作,一个进程可以对另一个进程执行这些操作,并将它们绑定到一个名称。<operation> 可以包含 <input>、<output> 和 <fault> 元素。对于所有这三个元素,message 属性引用 WSDL 文件中所定义的 <message> 元素。<input> 元素声明客户机向 Web 服务请求传输的需求。<output> 声明 Web 服务响应的内容。<fault> 元素描述当 Web 服务设法响应客户机的请求时所发生的任何消息级异常。请参阅下面的图和示例。
<wsdl:definitions
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
...>
...
<wsdl:portType name="DVDRenting">
<wsdl:operation name="SubscribeToSpecialsAlertList">
<wsdl:input message="tns:Subscribe" />
</wsdl:operation>
<wsdl:operation name="RentDVD">
<wsdl:input message="tns:RentalRequest" />
<wsdl:output message="tns:PurchaseResponse" />
<wsdl:fault name="tns:RentFault" message="tns:PaymentFault" />
</wsdl:operation>
</wsdl:portType>
<wsdl:portType name="DVDPurchasing">
<wsdl:operation name="BuyDVD">
<wsdl:input message="tns:PurchaseRequest" />
<wsdl:output message="tns:PurchaseResponse" />
<wsdl:fault name="tns:PurchaseFault" message="tns:PaymentFault" />
</wsdl:operation>
</wsdl:portType>
...
</wsdl:definitions>
<binding> 元素
就某个具体的传输或消息传递协议而言(即 SOAP、SMTP 和 HTTP 等等),到目前为止所列出的 WSDL 元素都是抽象的。使用任何协议的组织都可以实现上面所述的 Web 服务接口。WSDL <binding> 元素的内容将这些抽象的钩接点与 Web 协议束缚起来。binding 元素既有 name 属性(用来在 WSDL 文档内标识该绑定),又有 type 属性(用来引用该元素描述绑定时所针对的 portType)。它还有 <operation> 元素,与它所绑定的 <portType> 中的每个 <operation> 元素对应。在 <operation> 元素又有 <input>/<output>/<fault> 元素,与相应的 <operation> 元素中所定义的元素相对应。描述绑定的元素嵌套在 <binding> 元素的这些子元素之中,这是为了将消息传递协议的详细内容链接到目标 portType 中所提到的通则上。示例如下。
<wsdl:definitions>
...
<wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
<wsdl:operation name="tns:SubscribeToSpecialsAlertList">
<wsdl:input>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="tns:RentDVD">
<wsdl:input>
</wsdl:input>
<wsdl:output>
</wsdl:output>
<wsdl:fault>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
...
</wsdl:definitions>
W3C 推荐了三个 Web 服务的绑定:HTTP 上的 SOAP(SOAP over HTTP)、HTTP GET/POST 和 SOAP/MIME。我们将研究 HTTP 上的 SOAP,因为它似乎是最流行的。在尝试为 DVDRenting 协议创建 SOAP 绑定的过程中可能会出现一些问题:
- 应该把 RentDVD 的输入值放在什么地方?放在 SOAP 头(Header)中吗?
- SubscribeToSpecialsAlertList 输入值中的数据需要遵守 SOAP 编码规则吗?换言之它可以是任意的 XML 文件(请参阅编码样式)吗?
- 在需要通知故障消息时,应该将 RentDVD 出错放在 SOAP 消息中的哪个位置?
这些问题由 SOAP 绑定来回答:
<wsdl:definitions>
...
<wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="SubscribeToSpecialsAlertList">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="RentDVD">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:output>
<wsdl:fault name="tns:RentFault">
<soap:fault namespace="http://dvd.com" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
...
</wsdl:definitions>
<soap:binding> 元素表示 soap 通信的消息传递样式(rpc)(请参阅“消息传递样式”)和期望使用的传输协议(HTTP)。<soap:operation> 元素的 soapAction 属性被转换成 HTTP 头,该头声明了将被发送的 HTTP 请求的目的。在 SOAP 1.1 中引入了这个头,以允许防火墙和其它预处理节点对 SOAP 请求进行过滤。在 SOAP 1.2 中,不赞成使用这个头,而赞成将请求中的请求目的声明为一个参数,称作“action”。因而通常就让这个头空着。<soap:body> 和 <soap:fault> 元素表示消息数据应当放到所生成的 SOAP <Envelope> 中的什么地方。在上面的案例中,除了 RentDVD 出错数据应当包含在 SOAP <Fault> 元素中之外,其它所有消息都将包含在常规的 SOAP <Body> 元素中。
<service> 元素
最后,<service> WSDL 元素将某个具体的绑定与网络上的一个或多个进程相关联,这些进程可以根据绑定所实现的 portType 来处理请求。对于 HTTP 上的 SOAP,这就是指向那个进程的 URL。
<wsdl:service name="DVDRentalServices">
<wsdl:documentation>Here are some endpoints that support
the DVDRental SOAP HTTPbinding</wsdl:documentation>
<wsdl:port name="LPCDVDRentalService" binding="tns:SoapDVDRenting">
<soap:address location="http://localhost:6080/soap/servlet/rpcrouter" />
</wsdl:port>
<wsdl:port name="ClassicsDVDRentals" binding="tns:SoapDVDRenting">
<soap:address location="http://www.classicsdvdrentals.com/soap/servlet/rpcrouter" />
</wsdl:port>
</wsdl:service>
关于消息传递样式
SOAP <Body> 元素可以描述的消息有两种:描述远程过程调用(rpc
)的 XML 文档或任意 XML 文档(document
)。
在 RPC 样式的消息传递情况下,两个进程之间发生请求-响应类型的通信是很常见的。也就是说,客户机进程调用服务器进程上的一个过程,将该过程所需的所有参数传递给它,服务器进程将它拥有的所有返回值发送回客户机进程。该过程的名称、参数以及返回类型都在 SOAP <Body> 元素中以 XML 进行描述。
文档样式的消息传递表示 SOAP <Body> 元素的内容是任意的 XML 文档。尽管可以在请求-响应类型的通信方案中使用文档样式的消息传递,但是在异步通信中使用它非常理想,因为这个自包含的 XML 文档可以放入队列等待处理。
在下面的示例中,消息传递样式是通过使用 <soap:binding> 元素的 style 属性来声明的。在本例中,样式是 rpc
。而 style 属性另一个可能的值是 document
。
<wsdl:definitions>
...
<wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="tns:SubscribeToSpecialsAlertList">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="tns:RentDVD">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:output>
<wsdl:fault>
<soap:fault namespace="http://dvd.com" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
...
<wsdl:definitions>
关于编码
通过线路发送的 XML 流(在 SOAP 消息主体中)可以使用以下两种编码之一:encoded
或 literal
。要理解这两者之间的差异,您必须先理解在进程中处理 XML 流可能采用的不同方式。
所有格式良好的 XML 文档都可以被数据编出到一个 DOM 树中。一旦创建了树,线程可以遍历 DOM 树并递归地读取/修改它。按照开发人员的需要,可以对 DOM 树中类型的引用进行数据类型转换,也可以转换 DOM 树中的数据(例如,将字符串“10”转换成整数 10)。任意 XML 文档中数据所预期的数据类型,都可以在相关的 XML Schema 文档中(更笼统地说,是在 DTD 中)定义。编码样式 literal
表示可以直接将 SOAP <Body> 元素内容的值转换成 DOM 树,而不必担心它们确切的编程类型。如果需要知道它们预期的类型,可以通过引用 XML 文档的模式来发现。
SOAP 1.1 建议书定义了一组编码规则,它们描述应当如何将数据序列化成 XML 流,以及如何将数据反序列化成编程类型。编码样式 encoded
表明 SOAP 传输的消息内容应遵循这些序列化/反序列化规则。
literal
编码的优点是没有对正在传输的 XML 文档作任何限制。它的编码依赖于模式,因此是完全可扩展的。由于这个原因,literal 编码逐渐成为传输 SOAP <Body> 内容比较常用的方式。
最后,如果客户机和服务器的开发人员都在使用供应商提供的工具来生成助手代码,那么,从这两类开发人员的角度看,所选择的编码样式应该不是可辨别的。
在下面的示例中,所有的 SOAP 消息都是用 literal 编码的,不需要遵循 SOAP 编码规则。编码是通过使用 <soap:body> 和 <soap:fault> 元素的“use”属性来声明的。“use”属性的另一个可能的值是 encoded
。
<wsdl:definitions>
...
<wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="tns:SubscribeToSpecialsAlertList">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="tns:RentDVD">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body namespace="http://dvd.com" use="literal"/>
</wsdl:output>
<wsdl:fault>
<soap:fault namespace="http://dvd.com" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
...
<wsdl:definitions>
WSDL2WebService
简介
WSDK 附带了 WSDL2WebService
(位于 %WSDK_HOME%\bin 目录),用来帮助您通过 WSDL 构建 Web 服务及其客户机运行时代码。WSDL2WebService
工具将使用 WSDL 文件生成创建 Web 服务所需的全部服务器代码,该 Web 服务是在给定的 WSDL 文件中定义的。它将为接口中声明的类型生成类,还将生成与客户机交互的接口。这样,开发人员只需负责提供要作为 Web 服务公开的业务逻辑即可。开发人员要实现的服务器模板代码有两种可能的形式:BEAN
或 EJB
,这取决于 type
标志的设置。如果是 BEAN
,开发人员只需要在一个简单的 Java 源文件中实现方法定义。完成的 Web 服务随即被部署为 Web 应用程序。如果类型是 EJB
,那就需要开发人员在一个无状态会话 Bean 中实现业务方法定义。
WSDL2WebService
工具在许多情况下都会发现它自己是有用的。例如:
- 您所在的行业已经通过 WSDL 定义了一组类型和接口,而您的公司希望自己实现它们。
- 在这种方案下,您可以获取该行业已定义的 WSDL 文件,并将其传递给
WSDL2WebService
工具。它会生成所需的助手代码和模板,而您需要做的只是在其中实现业务方法。被配置成与 WSDL 文件中定义的服务器接口通信的所有客户机随后将能与您的进程通信了!
- 在这种方案下,您可以获取该行业已定义的 WSDL 文件,并将其传递给
- 您正在设计新的服务器端组件,并且您想把它公开为 Web 服务。
- 在这种情况下,您可以使用 WSDL 定义组件的接口,然后使用
WSDL2WebService
工具生成所需的(您的组件逻辑可以实现的)类型和模板。
- 在这种情况下,您可以使用 WSDL 定义组件的接口,然后使用
- 您的组织有两个进程,您希望它们能够相互通信。
- 创建定义接口的 WSDL 文件,该接口由服务器进程公开。
WSDL2WebService
不仅将生成服务器助手代码和模板,还将生成客户机进程与服务器进程通信所需的客户机运行时代码。
- 创建定义接口的 WSDL 文件,该接口由服务器进程公开。
创建 Web 服务助手代码
使用 WSDL2WebService
创建可部署的 Web 服务的过程由两步组成。首先,WSDL2WebService
需要一个 WSDL 文件,它将通过该文件生成助手代码和模板代码:
WSDL2WebService -type <service type> -verbose -createService
<service name> -project <project dir> <wsdl file>
例如,
WSDL2WebService -type EJB -verbose -createService myservice
-project .\myserviceroot MyService.wsdl
正如前面提到的,<code>WSDL2WebService</code> 工具可以生成基于 Bean 或基于 EJB 的服务器组件的模板。可以将 <code>type</code> 标志设置成 <code>EJB</code>(象在示例中那样)或 <code>BEAN</code>。如果它的值是 <code>EJB</code>,那么将生成一个远程模板、一个 home 模板和一个 bean 模板,这些模板的名称将根据 WSDL 文件中的目标绑定名称而定。例如,如果在 <code>MyService.wsdl</code> 中,目标绑定与下面类似:
...
<wsdl:binding name="MyServiceBinding" type="tns:MyServicePortType">
...
</wsdl:binding>
...
对所生成的 EJB 类命名如下:
- home ― MyServicePortTypeHome.java
- 远程 ― MyServicePortType.java
- EJB 类 ― MyServiceBindingImpl.java
verbose
标志告诉 WSDL2WebService
以详细方式运行。这是可选的。
createService
标志的值是 Web 服务的根名称。
project
标志值告诉 WSDL2WebService
在哪里构建 Web 服务。
WSDL2WebService
工具在缺省情况下将生成客户机运行时代码。可选的 server-side-only
标志将关闭这一自动行为,导致只生成服务器所需的代码。
提供实现
WSDL2WebService
创建了模板 ― 提供给服务器端开发人员的快捷方法。该模板 Java 文件的名称与目标绑定名称相同。例如,为下列 WSDL 文件生成的模板文件应该是 LPCDVDRentalServiceSoapBindingImpl.java
(以下是其部分摘录)
<wsdl:service name="DVDRentalServices">
<wsdl:documentation>Here are some endpoints that support the DVDRental SOAP
HTTPbinding</wsdl:documentation>
<wsdl:port binding="tns:SoapDVDRenting" name="LPCDVDRentalService">
<soap:address location="http://localhost:6080/soap/servlet/rpcrouter" />
</wsdl:port>
</wsdl:service>
LPCDVDRentalServiceSoapBindingImpl.java
会有空的业务方法,其特征符对应于与绑定相关联的操作,针对该绑定实现了 Web 服务。开发人员负责“填充”这些方法。这通常是相当简单的步骤,因为创建 Web 服务的整个目的无非是公开现有服务器端功能或创建一些全新的事物,这两种目的出现的频率基本相同。以下代码是通过 WSDL2WebService
工具生成的模板代码的示例。
package com.dvd;
public class SoapDVDRentingImpl implements com.dvd.DVDRenting{
public void subscribeToSpecialsAlertList(
com.dvd.EmailAddressType email, java.util.Date until)
throws java.rmi.RemoteException {
}
public void rentDVD(com.dvd.DVDRentalType rentalRequest,
com.dvd.holders.ResponseConfirmationTypeHolder paymentResponse,
org.apache.axis.holders.DateHolder purchaseDate)
throws java.rmi.RemoteException, com.dvd.PaymentFault {
paymentResponse.value = new com.dvd.ResponseConfirmationType();
purchaseDate.value = new java.util.Date();
}
}
我们省略了 subscribeToSpecialsAlertList(...)
和 rentDVD(...)
的实现。Java 类型com.dvd.EmailAddressType
、.com.dvd.DVDRentalType
和 com.dvd.holders.ResponseConfirmationTypeHolder
由 WSDL2WebService
工具生成。它们对应于源 WSDL 文件中定义的模式类型。请注意,不需要知道在“幕后”使用的传输或消息传递协议。处理数据的序列化和反序列化的是与 WSDK 一起提供的运行时代码 JAR 文件和由 WSDL2WebService
通过源 WSDL 生成的代码。
创建可部署的 EAR 文件
将 Web 服务生成为 EAR 文件并部署它,这是使用 WSDL2WebService
工具的第二个步骤。下面显示了用来从 Web 服务创建 EAR 文件并部署该文件的命令:
WSDL2WebService -deploy -verbose -createEar <EAR name> -project<project dir>
例如,
WSDL2WebService -deploy -verbose -createEar myservice.ear -project .\myserviceroot
执行该命令后,在指定的目录中创建了 EAR 文件(在上例中,在当前目录中创建了 myservice.ear),并将其部署到与 WSDK 一起提供的WebSphere Application Server Express。为了使部署(deploy)标志起作用,必须启动服务器。虽然此时会部署这个 EAR 文件,但不会启动该文件。为了使客户机与该 Web 服务交互,必须启动该 EAR 文件。要启动 Web 应用程序,打开命令提示符,然后转至%WSDK_HOME%\bin。命令 appserver
将列出所有已部署的应用程序及其状态。要启动您的 Web 服务,从列表中找出它的名称,然后输入命令
appstatusappserver startapp <ws name>
,其中 <ws name> 是该 Web 服务的名称。
例如,如果下面是执行 appserver appstatus 命令后的输出:
Installed applications
Sample1WebService (disabled/stopped)
Sample2EJBClient (enabled/running)
Sample2WebService (disabled/stopped)
Sample4WebService (disabled/stopped)
Sample5WebService (disabled/stopped)
Sample6WebService (disabled/stopped)
Sample7WebService (disabled/stopped)
Sample8WebService (disabled/stopped)
UDDIRegistry (enabled/running)
myservice (enabled/stopped)
要启动名称为 myservice 的 Web 服务,可以在命令行上输入:
appserver
startapp myservice
实验 1:构建和部署 Web 服务
实验目的和概述
本实验的目的是向您提供使用 IBM WSDK 和 WSDL2WebService
工具构建、部署和测试 Java Web 服务的实践经验。本实验还将演示 WSDK 使您从 WSDL Web 服务定义创建客户机和服务器代码的工作变得多么简单。
该实验有四个非常简单的步骤:
- 使用
WSDL2WebService
通过 WSDL 文件生成客户机和服务器的助手代码。 - 在生成的服务器模板中实现业务方法。
- 编译 Web 服务源代码,然后使用
WSDL2WebService
将其打包成 EAR 文件并部署它。 - 创建一个客户机以测试该服务。
通过 WSDL 文件生成代码
在本章中,您将生成 Web 服务及其客户机的代码。所生成的代码当然会缺少其业务逻辑,但我们会在下一页中添加它。所生成的代码将基于下面这个简单的 WSDL。它看起来应该比较熟悉。因为它描述了在教程通过 Java 类创建 Web 服务(Creating a Web service from a Java class)的实验 1 中创建的 rpc Web 服务。
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="http://server.simplewsdl.dvdonline.lpc"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://server.simplewsdl.dvdonline.lpc"
xmlns:intf="http://server.simplewsdl.dvdonline.lpc"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types/>
<wsdl:message name="getDVDResponse">
<wsdl:part name="getDVDReturn" type="xsd:string"/>
</wsdl:message>
<wsdl:message name="getDVDRequest">
</wsdl:message>
<wsdl:portType name="DVDOnlineStore_SEI">
<wsdl:operation name="getDVD">
<wsdl:input message="intf:getDVDRequest" name="getDVDRequest"/>
<wsdl:output message="intf:getDVDResponse" name="getDVDResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="DVDOnlineStoreSoapBinding" type="intf:DVDOnlineStore_SEI">
<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getDVD">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="getDVDRequest">
<wsdlsoap:body namespace="http://server.simplewsdl.dvdonline.lpc" use="literal"/>
</wsdl:input>
<wsdl:output name="getDVDResponse">
<wsdlsoap:body namespace="http://server.simplewsdl.dvdonline.lpc" use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="DVDOnlineStore_SEIService">
<wsdl:port binding="intf:DVDOnlineStoreSoapBinding" name="DVDOnlineStore">
<wsdlsoap:address location="http://localhost:6080/simpleWSDL/services/DVDOnlineStore"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
要生成助手代码,请执行以下步骤:
- 在 %WSDK_HOME%\apps 下创建名为 simpleWSDL 的目录。
- 将以上的 WSDL 复制到新创建的 simpleWSDL 目录下名为 DVDOnlineStoreSimple.wsdl 的文件中。
- 在命令行中,转至新创建的 simpleWSDL 目录,然后输入:
%WSDK_HOME%\bin\WSDL2WebService
-createService simpleWSDL -project root DVDOnlineStoreSimple.wsdl
运行该命令的结果是创建了一个名为 root 的目录。在该目录下,是为客户机和服务器生成的全部代码。客户机代码在root\simpleWSDL\client-side 目录下。服务器代码在 root\simpleWSDL\lpc 目录下。另外还在 root\simpleWSDL 下创建了一个 compile.bat,您以后可以用它来编译源代码。
实现模板服务器代码
WSDL2WebService 创建名为%WSDK_HOME%\apps\simpleWSDL\root\simpleWSDL\lpc\dvdonline\simplewsdl\server\DVDOnlineStoreSoapBindingImpl.java 的文件。这个文件是一个模板,该模板的业务方法特征符与在 WSDL 文件中定义的特征符匹配。在本例中,只有一个方法,因为在 WSDL 文件中只声明了一个操作。下面的代码清单是这个 DVDOnlineStoreSoapBindingImpl.java
,其中有 getDVD()
方法的建议实现:
package lpc.dvdonline.complexwsdl.server;
import lpc.dvdonline.complexwsdl.service.DVD;
public class DVDOnlineStoreSoapBindingImpl
implements lpc.dvdonline.complexwsdl.server.DVDOnlineStore_SEI{
public lpc.dvdonline.complexwsdl.service.DVD getDVD()
throws java.rmi.RemoteException {
DVD dvd = new DVD();
dvd.setRank(10);
dvd.setTitle("Fight Club");
dvd.setCategory("Weird but good");
return dvd;
}
}
要编译该源代码以及生成的其它服务器类,转至 %WSDK_HOME%\apps\simpleWSDL\root\simpleWSDL,然后执行:
compile.bat
打包、部署和启动服务
要把这个简单的类变成 Web 服务还要做很多工作。幸运的是,WSDK 有帮助您做这些工作的工具。要打包并部署您的服务,只需:
1. 启动应用程序服务器
- 您可以从开始菜单启动应用程序服务器。WSDK 安装程序在 WSDK 程序组中创建名为 Start Server 的快捷方式,您可以用它来启动服务器。
- 您可以从命令行启动 WebSphere Application Server。它位于 bin 目录中,可以这样调用它:
%WSDK_HOME%\bin\appserver
。
start
启动应用程序服务器。当应用程序服务器装入时,您可以看到几条状态消息。寻找消息“Server server1 open for e-business”。
2. 运行 WSDL2WebService 实用程序
- 在第一步中,该工具生成了服务器代码。现在,它将打包并部署 Web 服务应用程序。
- 注:在调用该工具之前,请确保 WSDK 的 bin 目录(%WSDK_HOME%\bin)在您的路径中。
- 要运行它,可打开一个命令提示符,转至 %WSDK_HOME%\apps\simpleWSDL 目录,然后用带有
deploy
和createEar
标志的命令调用WSDL2WebService
实用程序:WSDL2WebService
-deploy -createEar simpleWSDL.ear -project root
用于 WSDL2WebService
的参数如下:
-
deploy
标志使得产生的 EAR 文件(包括类文件和部署描述符)将被部署到应用程序服务器。 -
createEar
标志定义 EAR 文件的名称,并定义 Web 服务通信的 Web 环境。 -
project
标志告诉工具 Web 服务类文件和 xml 文件的根目录在哪里。
您无需对 simpleWSDL.ear 进行任何操作,因为这里使用了 deploy
标志。应用程序已得到部署,它的一个已展开格式的副本被放在appserver
目录下。
3. 检查结果
- 通过检查服务器日志文件中是否有错误以及通过使用
appserver
命令,来验证服务已得到部署。
appstatusappserver appstatus
命令列出 WebSphere Application Server 中已安装的企业应用程序,以及它们的运行status (running | stopped)
。如下调用appserver appstatus
:appserver appstatus
在已安装企业应用程序的列表中查找 simpleWSDL 应用程序名称。
4. 启动 Web 服务
- 使用
appserver startapp
命令启动 Web 服务,如下所示:appserver startapp
simpleWSDL
该命令告诉 WebSphere 启动与企业应用程序 simpleWSDL 相关的 Web 服务。
创建 simpleWSDL 的服务客户机
既然有了服务,就需要客户机。创建客户机非常简单。只需要几行代码。
- 在 %WSDK_HOME%\apps\simpleWSDL 目录中,创建名为 requester 的目录。该目录将包含客户机源代码。
- 在 requester 目录中,创建名为 src 的目录。
- 在 src 目录中,创建一系列反映包结构
lpc.dvdonline.simplewsdl.client
的目录。 - 在包 lpc\dvdonline\simplewsdl\client 目录中,创建名为
DVDOnlineStoreClient.java
的 Java 源文件。将以下代码添加到源文件中:package lpc.dvdonline.simplewsdl.client;
import lpc.dvdonline.simplewsdl.server.*;
public class DVDOnlineStoreClient {
public static void main( String[] args ) throws Exception{
DVDOnlineStore_SEIService loc = new DVDOnlineStore_SEIServiceLocator();
DVDOnlineStore_SEI port = (DVDOnlineStore_SEI)loc.getDVDOnlineStore();
System.out.println( port.getDVD() );
} //end main()
} //end DVDOnlineStoreClient
请注意,这个客户机创建了一个服务。它使用特定于 IBM 的类型ServiceLocator
来完成这一工作。它通过服务访问端口(即 Web 服务的接口),然后调用getDVD()
方法。 - 客户机需要由
WSDL2WebService
生成的存根、接口和定位器类。将 %WSDK_HOME%\apps\simpleWSDL\root\simpleWSDL\client-side 目录中的文件和目录复制到 %WSDK_HOME%\apps\simpleWSDL\root\requester\src 目录中。 - 此时就可以着手进行编译了。和以前一样编译以上类,前提是类路径中包含 %WSDK_HOME%\appserver\lib 中以下的 JAR 文件:
- j2ee.jar
- axis.jar
- qname.jar
- jaxrpc.jar
- wsdl4j.jar
- xerces.jar
- commons-discovery.jar
- saaj.jar
- commons-logging-api.jar
- 使用 java 命令运行客户机。确保类路径上的 JAR 文件和用于编译时类路径上的 JAR 文件相同。
java -classpath
... lpc.dvdonline.simplewsdl.client.DVDOnlineStoreClient - 如果您已开始使用 Eclipse IDE,则只需单击工具栏上的小“奔跑者”图标按钮。
如果您看到“Fight Club”的响应消息,那么您的 Web 服务已启动并正在运行。您创建、部署并启动了您的首个 Web 服务,接着您编写了一个客户机来调用该 Web 服务的方法。
实验回顾
让我们回顾一下您所做的工作,并回顾一下 WSDL2WebService
实用程序为您做了什么。
您获得了一个 WSDL 文件,WSDL2WebService
通过该文件生成了服务器模板和客户机代码。此后,您实现了所生成的服务器模板代码。然后,您使用提供的 compile.bat 编译了所有的代码。WSDL2WebService
将您的 Web 服务部署为捆绑在 WAR 文件中的 J2EE Web 应用程序,而这个 WAR 文件又捆绑在 EAR 文件中。您使用静态的、预先定义的存根类创建 Web 服务客户机。
虽然您没有亲自动手定义接口,但 WSDL2WebService
为您创建了一个,如下所示:
package lpc.dvdonline.simplewsdl.server;
public interface DVDOnlineStore_SEI extends java.rmi.Remote {
public java.lang.String getDVD() throws java.rmi.RemoteException;
}
如果您进行过 RMI 开发,就知道这个接口看上去和为 RMI 服务程序创建的接口类型完全相同。
WSDL2WebService
工具还生成一个服务类和一个存根类。服务类实现了 javax.xml.rpc.Service
接口。javax.xml.rpc.Service
是所生成的存根的工厂。对于这些生成文件的更深入的描述,请回头参阅教程通过 Java 类创建 Web 服务(Creating a Web service from a Java class)。
实验 2:交换复杂对象
实验概述
本实验的目的是演示 IBM WSDK 交换复杂 SOAP 数据类型的能力,这些数据类型是根据 WSDL 文件中的定义用 WSDL2WebService
工具生成的。
虽然传递字符串(String)是您开始使用 Web 服务的好方法,但实际的应用程序通常要求客户机和服务交换具有复杂数据结构的非标量类型的对象。在本实验中,您将遵循上个实验中相同的步骤,唯一不同的是您要开始处理的 WSDL 源文件中定义了一个复杂类型 ― DVD。服务器将把该 DVD 类型的实例传递给客户机。
定义表示 DVD 对象的复杂 XML Schema 类型
正如本教程前面讨论的,通过使用 XML Schema,您在 WSDL 文件中需要多复杂的类型,就可以完全*地定义该类型。以下 WSDL 文件和您在上个实验中处理的相同,唯一不同的是服务器向客户机返回 DVD 对象,而不只是字符串。该 DVD 类型在 WSDL 中的 <types> 元素中定义,并且在 <message> 元素中被引用。
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="http://server.complexwsdl.dvdonline.lpc"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://server.complexwsdl.dvdonline.lpc"
xmlns:intf="http://server.complexwsdl.dvdonline.lpc"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns2="http://service.complexwsdl.dvdonline.lpc"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace="http://service.complexwsdl.dvdonline.lpc"
xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="DVD">
<sequence>
<element name="category" nillable="true" type="xsd:string"/>
<element name="rank" type="xsd:int"/>
<element name="title" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="DVD" nillable="true" type="tns2:DVD"/>
</schema>
</wsdl:types>
<wsdl:message name="getDVDRequest">
</wsdl:message>
<wsdl:message name="getDVDResponse">
<wsdl:part name="getDVDReturn" type="tns2:DVD"/>
</wsdl:message>
<wsdl:portType name="DVDOnlineStore_SEI">
<wsdl:operation name="getDVD">
<wsdl:input message="impl:getDVDRequest" name="getDVDRequest"/>
<wsdl:output message="impl:getDVDResponse" name="getDVDResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="DVDOnlineStoreSoapBinding" type="impl:DVDOnlineStore_SEI">
<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getDVD">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="getDVDRequest">
<wsdlsoap:body namespace="http://server.complexwsdl.dvdonline.lpc" use="literal"/>
</wsdl:input>
<wsdl:output name="getDVDResponse">
<wsdlsoap:body namespace="http://server.complexwsdl.dvdonline.lpc" use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="DVDOnlineStore_SEIService">
<wsdl:port binding="impl:DVDOnlineStoreSoapBinding" name="DVDOnlineStore">
<wsdlsoap:address location="http://localhost:6080/compleWSDL/services/DVDOnlineStore"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
要生成助手代码,请执行以下步骤:
- 在 %WSDK_HOME%\apps 下创建名为 complexWSDL 的目录。
- 将上面的 WSDL 复制到新创建的 complexWSDL 目录下名为 DVDOnlineStoreComplex.wsdl 的文件中。
- 在命令行中,转至新创建的 complexWSDL 目录,然后输入:
%WSDK_HOME%\bin\WSDL2WebService
-createService complexWSDL -project root DVDOnlineStoreComplex.wsdl
运行该命令的结果是创建了一个名为 root 的目录。在该目录下,是为客户机和服务器生成的全部代码。客户机代码在root\complexWSDL\client-side 目录下。服务器代码在 root\complexWSDL\lpc 目录下。另外还在 root\complexWSDL 下创建了一个 compile.bat,您稍后可以用它来编译源代码。
实现模板服务器代码
WSDL2WebService 创建名为%WSDK_HOME%\apps\complexWSDL\root\complexWSDL\lpc\dvdonline\complexwsdl\server\DVDOnlineStoreSoapBindingImpl.java 的文件。和上一个实验不同的是,getDVD()
方法返回一个 DVD 实例。以下是 DVDOnlineStoreSoapBindingImpl.java
的代码清单,其中有 getDVD()
方法的建议实现:
package lpc.dvdonline.complexwsdl.server;
import lpc.dvdonline.complexwsdl.service.DVD;
public class DVDOnlineStoreSoapBindingImpl
implements lpc.dvdonline.complexwsdl.server.DVDOnlineStore_SEI{
public lpc.dvdonline.complexwsdl.service.DVD getDVD()
throws java.rmi.RemoteException {
DVD dvd = new DVD()
dvd.setRank(10);
dvd.setTitle("Fight Club");
dvd.setCatagory("Weird but good");
return dvd;
}
}
要编译该文件以及生成的其它服务器类,转至 %WSDK_HOME%\apps\complexWSDL\root\complexWSDL,然后执行:
compile.bat
。
打包、部署和启动服务
启动并运行该 Web 服务的步骤和上一个实验的步骤相同。我们再次列出这些步骤,但对名称作了适当的改动。
1. 启动应用程序服务器
- 您可以从开始菜单启动应用程序服务器。WSDK 安装程序在 WSDK 程序组中创建名为 Start Server 的快捷方式,您可以用它启动服务器。
- 您可以从命令行启动 WebSphere Application Server。启动命令位于 bin 目录中,可以这样调用它:
%WSDK_HOME%\bin\appserver
。
start
继续然后启动应用程序服务器。当应用程序服务器装入时,您可以看到几条状态消息。寻找消息“Server server1 open for e-business”。
2. 运行 WSDL2WebService 实用程序
- 在第一步中,该工具生成了服务器代码。现在,它打包这个 Web 服务应用程序,并部署它。
- 注:在调用该工具之前,请确保 WSDK 的 bin 目录(%WSDK_HOME%\bin)在您的路径中。
- 要运行它,可打开一个命令提示符,转至 %WSDK_HOME%\apps\complexWSDL 目录,然后用带有
deploy
和createEar
标志的命令调用WSDL2WebService
实用程序:WSDL2WebService
-deploy -createEar complexWSDL.ear -project root
用于 WSDL2WebService
的参数和上一个实验相同。
您无需对 simpleWSDL.ear 进行任何操作,因为这里使用了 deploy
标志。应用程序已得到部署,它的一个已展开格式的副本被放置在appserver
目录下。
3. 检查结果
- 通过检查服务器日志文件中是否有错误以及通过使用
appserver
命令,来验证服务已得到部署。
appstatusappserver appstatus
命令列出 WebSphere Application Server 中已安装的企业应用程序,以及它们的运行status (running | stopped)
。如下调用appserver appstatus
:appserver appstatus
在已安装企业应用程序的列表中查找 complexWSDL 应用程序的名称。
4. 启动 Web 服务
- 使用
appserver startapp
命令启动 Web 服务,如下所示:appserver startapp
complexWSDL
该命令告诉 WebSphere 启动与企业应用程序 complexWSDL 相关联的 Web 服务。
创建 complexWSDL 的服务客户机
我们只需稍微修改一下上个实验中的客户机,以便使它与 DVD Web 服务通信。
- 在 %WSDK_HOME%\apps\complexWSDL 目录中,创建名为 requester 的目录。该目录将包含客户机源代码。
- 在 requester 目录中,创建名为 src 的目录。
- 在 src 目录中,创建一系列反映包结构
lpc.dvdonline.complexwsdl.client
的目录。 - 在包 lpc\dvdonline\complexwsdl\client 目录中,创建名为
DVDOnlineStoreClient.java
的 java 源文件。将以下代码添加到源文件中:package lpc.dvdonline.complexwsdl.client;
import lpc.dvdonline.complexwsdl.server.*;
public class DVDOnlineStoreClient {
public static void main( String[] args ) throws Exception{
DVDOnlineStore_SEIService loc = new DVDOnlineStore_SEIServiceLocator();
DVDOnlineStore_SEI port = (DVDOnlineStore_SEI)loc.getDVDOnlineStore();
DVD dvd = port.getDVD();
System.out.println(dvd.getTitle());
} //end main()
} //end DVDOnlineStoreClient
请注意,这个客户机与前面的一样,唯一区别是返回给它的(由 Web 服务发送)不是字符串而是 DVD 对象。 - 客户机需要由
WSDL2WebService
生成的存根、接口和定位器类。将 %WSDK_HOME%\apps\complexWSDL\root\dvdonline\client-side 目录中的文件和目录复制到 %WSDK_HOME%\apps\complexWSDL\root\requester\src 目录中。 - 此时就可以着手进行编译了。和前面一样编译以上类,前提是类路径中包含 %WSDK_HOME%\appserver\lib 中的以下 JAR 文件:
- j2ee.jar
- axis.jar
- qname.jar
- jaxrpc.jar
- wsdl4j.jar
- xerces.jar
- commons-discovery.jar
- saaj.jar
- commons-logging-api.jar
- 使用 java 命令运行客户机。确保类路径上的 JAR 文件和用于编译时类路径上的文件相同。
java -classpath
... lpc.dvdonline.complexwsdl.client.DVDOnlineStoreClient - 如果您已开始使用 Eclipse IDE,则只需单击工具栏上的小“奔跑者”图标按钮。
同样,如果您看到“Fight Club”的响应消息,那么您的 Web 服务就已启动并运行了。您创建、部署并启动一个使用复杂类型的 Web 服务,接着您编写了一个客户机来调用该 Web 服务的方法。正如您所见,主要差异在 WSDL 中。大多数复杂性都对您隐藏起来。
教程结束语
教程回顾
在本教程中,我们完成了以下工作:
- 回顾 WSDL 并讨论它之所以重要的原因。
- 了解 WSDL 结构和 WSDL 元素。
- 回顾 XML Schema,并了解如何将它和 WSDL 一起使用。
- 使用 WSDL2WebService 通过 WSDL 文件创建基于 Web 服务的系统。
- 创建、部署并使用通过 WSDL 文件创建的小型 Web 服务系统。
WSDL 乍一看似乎有些深奥,您可能认为它是 Web 服务体系结构中无关紧要的部分。您确实无需直接使用 WSDL 就可以进行大量的 Web 服务开发 ― 只要您有适当的高级工具,但理解 WSDL 的结构和它的用途很重要。它是有关 Web 服务互操作性的关键部分。如果使用 UDDI 注册中心的话,WSDL 也很重要。
我们让您了解了 WSDL 是什么,以及如何用 IBM WSDK 来使用它。如果您在 Web 服务方面的工作不断增加的话,它会对您非常有帮助。
参考资料
学习
- SOA and Web services 专区:提供了大量的文章,以及关于如何开发 Web 服务应用程序的初级、中级和高级教程。