内容


Web 服务互操作性,第 1 部分

Comments

引言


在谈到把为不同供应商的平台写的应用程序(可能是用不同编程语言写的并且在不同操作系统上)连接在一起时,Web 服务技术很有可能会提供大力帮助。一般说来,“Web 服务”只是软件的一部分,这些软件我们可以通过在网络上向它们发送 XML 格式的消息来访问。XML 并不局限于某种特定的编程语言或操作系统,因而,它可以被用作异构系统间的信息交换协议。Web 服务定义语言(Web Services Definition Language,WSDL)可以用来描述 Web 服务提供的接口,它也是基于 XML。

一旦基础架构到位,就会出现新的机会使我们能够连接不同的平台和环境来实现业务流程。但是,由于技术和技术的底层规范以及标准还比较新,所以必须注意确保来自不同供应商的平台可以适当地通信。

应用案例


为这次演示选择的应用程序实现一个订单案例。在这个案例中,一个潜在的买方(即“顾客”)检索一份由特定的卖方提供的产品目录并选择要购买的产品及数量。当一次购买完成后,一份带有买方要求的产品类型和数量的订单就被发送到供应商。

然后供应商将检查是否有满足要求的产品可发送给顾客。供应商可以通过查询仓库来检查。如果没有足够的库存来满足订单的需要,就会适当地设置订单的状态并返回一个错误。如果有足够的可用库存,那么供应商将检查顾客的信誉来执行这次购买。每个买方都与一个特定的银行联系在一起,该银行可以提供买方的帐户状态信息,还可以从该帐户扣除所需资金。

最后,假设买方的银行帐户状态良好,供应商就向仓库发送一个按订单发货的请求。同时,顾客可以通过供应商检查这个订单的状态,或者获得确认发货的发票。

目前为止,参与这种应用程序的供应商有 IBM、Amberpoint、IONA 和 webMethods。随着时间的推移,这个供应商列表可能会发生改变。配置文件和 WSDL 定义放在一个由 xmethods.net 运营的站点 http://www.xmethods.net/wsid/online上。

角色


为了能够测试各种应用程序,角色被定义为可以配置成运行在不同的机器上,这些机器由参与该演示的不同供应商提供。上述应用案例中的每一个参与者(即顾客(Customer)、供应商(Supplier)、仓库(Warehouse)和银行(Bank))都是被作为 Web 服务实现的。这些服务可以用不同的实现运行在同一台机器或不同的机器上,这显示了 Web 服务技术在异构环境中的价值。

图 1显示了应用程序涉及的角色以及这些角色各自支持的操作。

角色和操作
角色和操作

图 1归 XMethods 版权所有 ©,这里的使用已获授权)。

运行应用程序时使用角色的哪一个实现是由一组描述组合(例如,一个特定的仓库服务和一个特定的供应商是一个组合)的 XML 文件决定的。这样就可以配置不同供应商提供的角色实现的任意排列组合。XML 配置文件放在中央,一般不可以改动。将来对应用程序的加强也许会允许在本地改动。

Web 服务


我们刚才已经提到过,购买案例中的每一个角色都被作为 Web 服务来实现。这意味着这些角色可以用 WSDL 中定义的服务接口来代表。每个服务封装特定的业务功能,这些业务功能可以通过一种公共机制(即 HTTP 上的 SOAP)来调用。

该应用程序的一个目标就是允许使用不同的服务实现,以此来测试和演示不同 Web 服务环境之间的互操作性。这意味着每个参与的供应商都会为每个服务提供一个实现。在运行应用程序时实际使用哪个服务是在运行时决定的。所有的服务实现支持同一个抽象接口,这样客户机就可以用完全相同的方法来调用它们中的每一个。这种方法的另一个作用是,您可以看到如何不通过硬编码服务的位置构建 Web 服务的客户机。

WSDL 通过提供包含这些信息的不同元素来支持接口和实现的分离。端口在 <portType> 元素中描述,该元素定义服务提供的操作。例如,测试应用程序有一个仓库服务(Warehouse service),该服务带有 checkAvailability、shipRequest 和 shipConfirm 三个操作。这个仓库服务的 WSDL 文档在一个 portType 中定义这些操作,并在一个 XML Schema 中定义这些操作使用的数据类型。 清单 1显示了仓库服务的完整 WSDL 定义。

清单 1. 仓库服务的 WSDL 定义

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="IWarehouse" 
targetNamespace="http://www.xmethods.net/ws-demo/" 
xmlns:tns="http://www.xmethods.net/ws-demo/" 
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
xmlns:xsd=http://www.w3.org/2001/XMLSchema
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/">
 <types>
  <schema xmlns=http://www.w3.org/2001/XMLSchema 
  targetNamespace="http://www.xmethods.net/ws-demo/"
  xmlns:tns="http://www.xmethods.net/ws-demo/">
    <complexType name="PO">
     <sequence>
    <element name="header" nillable="true" type="tns:POHeader"/>
    <element name="lines" nillable="true" type="tns:ArrayOfPOLine"/>
   </sequence>
     </complexType>
     <complexType name="POHeader">
   <sequence>
    <element name="PONumber" nillable="false" type="string"/>
    <element name="timestamp" nillable="false" type="dateTime"/>
    <element name="supplierID" nillable="false" type="string"/>
    <element name="customerID" nillable="false" type="string"/>
    <element name="notes" nillable="true" type="string"/>
    <element name="numberOfLines" nillable="false" type="int"/>
    <element name="subtotal" nillable="false" type="float"/>
    <element name="shipToLine1" nillable="false" type="string"/>
    <element name="shipToLine2" nillable="true" type="string"/>
    <element name="shipToLine3" nillable="true" type="string"/>
    <element name="shipToCity" nillable="false" type="string"/>
    <element name="shipToState" nillable="false" type="string"/>
    <element name="shipToPostalCode" nillable="false" type="string"/>
    <element name="shipToCountry" nillable="true" type="string"/>
    </sequence>
     </complexType>
     <complexType name="POLine">
    <sequence>
    <element name="lineNumber" nillable="false" type="int"/>
    <element name="itemNumber" nillable="false" type="string"/>
    <element name="itemName" nillable="false" type="string"/>
    <element name="quantity" nillable="false" type="int"/>
    <element name="unitOfMeasure" nillable="false" type="string"/>
    <element name="unitPrice" nillable="false" type="float"/>
    <element name="lineTotal" nillable="false" type="float"/>
        </sequence>
     </complexType>
     <complexType name="ArrayOfPOLine">
     <complexContent>
    <restriction base="soapenc:Array">
     <attribute ref="soapenc:arrayType" 
     wsdl:arrayType="tns:POLine[]"/>
    </restriction>
   </complexContent>
   </complexType>
  </schema>
 </types>
 <message name="checkAvailability0SoapIn">
  <part name="po" type="tns:PO"/>
 </message>
 <message name="checkAvailability0SoapOut">
  <part name="Result" type="xsd:boolean"/>
 </message>
 <message name="shipRequest1SoapIn">
  <part name="po" type="tns:PO"/>
  <part name="confirmURL" type="xsd:string"/>
 </message>
 <message name="shipRequest1SoapOut"/>
 <portType name="IWarehouseSoap">
  <operation name="checkAvailability" parameterOrder="po">
   <input name="checkAvailability0SoapIn" 
   message="tns:checkAvailability0SoapIn"/>
   <output name="checkAvailability0SoapOut" 
   message="tns:checkAvailability0SoapOut"/>
  </operation>
  <operation name="shipRequest" parameterOrder="po confirmURL">
   <input name="shipRequest1SoapIn" 
   message="tns:shipRequest1SoapIn"/>
   <output name="shipRequest1SoapOut" 
   message="tns:shipRequest1SoapOut"/>
  </operation>
 </portType>
 <binding name="IWarehouseSoap" type="tns:IWarehouseSoap">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="checkAvailability">
   <soap:operation soapAction="" style="rpc"/>
   <input name="checkAvailability0SoapIn">
    <soap:body use="encoded" 
    namespace="http://www.xmethods.net/ws-demo/"
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   </input>
   <output name="checkAvailability0SoapOut">
    <soap:body use="encoded" 
    namespace=http://www.xmethods.net/ws-demo/
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   </output>
  </operation>
  <operation name="shipRequest">
   <soap:operation soapAction="" style="rpc"/>
   <input name="shipRequest1SoapIn">
    <soap:body use="encoded" 
    namespace="http://www.xmethods.net/ws-demo/"
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   </input>
   <output name="shipRequest1SoapOut">
    <soap:body use="encoded" 
    namespace="http://www.xmethods.net/ws-demo/"
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   </output>
  </operation>
 </binding>
</definitions>

请注意所使用的数据类型在 XML Schema 的 <types> 元素内是如何声明的。再一次,这又提供了一种独立于任何编程语言或平台的定义数据的方法。每种编程语言都有自己的方法来把这些定义映射到它特定的格式。

端点定义(也就是可访问某个特定服务的地址)存储在 WSDL 的 <service> 元素中,具体说来就是这个元素的 <port> 元素中。

不同的 Web 服务环境提供不同类型的 API 来调用服务。在这个案例中,我利用 Apache Axis 的开放源代码实现,它支持“Java API for XML based RPC”(或称为 JAX-RPC)。这个 API 提供一种机制,通过这种机制客户机可以动态地选择 Web 服务的地址然后向它发送请求。我们的示例实现使用这种机制来调用不同供应商提供的不同服务实现 - 包括以本地方式安装在 WebSphere Application Server 上的实现。

JAX-RPC 允许把 web 服务调用打包为一个所谓的“代理”或“存根”类(请参见 图 2)。这个类公开一个映射抽象服务接口的接口,从而向客户机隐藏 Web 服务调用的细节。这种方法也常用于传统的 J2EE 应用程序,在这种应用程序中通过客户机代理(它向它的客户机以本地方式提供 EJB 的方法)来访问远程 Enterprise JavaBean。我们的应用程序包含所有涉及的服务的代理。不管在运行时实际调用哪个服务实现都可以使用它们。

服务代理和服务实现
服务代理和服务实现

服务/端口配置


我们可以把应用程序配置为在运行时选择所需服务的不同实现。使用哪一个实现是通过很多 XML 配置文件定义的,这些文件指定这些服务的相互依赖性及地址。例如,一个仓库到一个特定供应商的映射是在一个名为 warehousemaps.xml 的文件中定义的(请参阅 清单 2)。

清单 2. warehousemaps.xml 文件示例

<?xml version="1.0" encoding="UTF-8"?>
<warehouseMaps>
 <map>
  <supplier>e2a-supplier</supplier>
  <warehouse>e2a-warehouse</warehouse>
 </map>
 <map>
  <supplier>ibm-supplier</supplier>
  <warehouse>ibm-warehouse</warehouse>
 </map>
 <map>
  <supplier>wm-supplier</supplier>
  <warehouse>wm-warehouse</warehouse>
 </map>
</warehouseMaps>

在这个示例中,IBM 提供的供应商将使用 IBM 仓库实现。同样,一个顾客到一个特定银行实现的映射是在一个名为 creditmaps.xml 的文件中定义的。

每个服务实现的具体端点在 roles.xml 文件中定义。这个文件为每个端点标识(上面提到的映射文件中使用了这种标识)分配一个 URL,然后代码在运行时使用这个 URL 向适当的服务实现发送请求。 清单 3显示 roles.xml 文件(该文件描述 IBM 提供的服务端点)中的一些样本条目。

清单 3. roles.xml 文件示例

<role participant="ibm">
 <type>supplier</type>
 <id>ibm-supplier</id>
 <endpoint>http://dwdemos.alphaworks.ibm.com/wsid/services/supplier</endpoint>
</role>
 <role participant="ibm">
 <type>customer</type>
 <id>ibm-customer</id>
</role>
 <role participant="ibm">
 <type>bank</type><id>ibm-bank</id>
 <endpoint>http://dwdemos.alphaworks.ibm.com/wsid/services/bank</endpoint>
</role>
 <role participant="ibm">
 <type>warehouse</type>
 <id>ibm-warehouse</id>
 <endpoint>http://dwdemos.alphaworks.ibm.com/wsid/services/warehouse</endpoint>
</role>

运行应用程序


为运行实际的应用程序,您需要从实现上述顾客角色的入口页面开始。要访问 IBM 提供的顾客(Customer),请转到 URL http://www.ibm.com/developerworks/demos/wsid/index.html并选择 Customer 页面的超链接。顾客的实现运行在 IBM WebSphere Application Server,版本 4 上。 图 3显示了 Customer 页面。

WSID Customer 页面
WSID Customer 页面

从这个初始屏幕,您可以选择想要测试的顾客和供应商。在 图 3中显示的标识映射前面我提到过的 roles.xml 文件中定义的标识。仓库和银行的实现可以从供应商定义获得,并且它们分别定义在 warehousemaps.xml 和 creditmaps.xml 文件中。注意:您也可以为顾客和供应商指定“local”。它的含义特定于每个供应商的实现 - 在本例中,它意味着您将使用 IBM 提供的供应商。这一系列的第二篇文章将向您展示如何在自己的本地 WebSphere 安装上运行应用程序。在本例中,为供应商指定“local”将把您指引到本地安装的供应商服务。

在您选择了想使用的顾客和供应商的标识之后,您可以通过单击“Get Catalog”按钮获得被选中供应商的目录。结果如 图 4所示。

供应商目录示例
供应商目录示例

现在输入关于您想购买的产品及数量的信息并单击“Send PO”按钮。它将促使供应商 web 服务去检查仓库和银行服务,然后确认或拒绝订单。如果订单被确认,将显示摘要页面,类似于 图 5

订单示例
订单示例

请用“Get Invoice”按钮来查看为您的特定订单生成的发票,使用“Get Status”按钮来检查是否已按订单发货(或许在订单确认到发货之间会发生延迟,这取决于服务实现)。

请用不同的顾客/供应商组合随意地运行应用程序几次。不管您选择哪一个供应商的实现,结果应该都是相同的。您也可以通过单击 Customer 页面上的各个按钮来列出您所有的发票、定义的端点和角色映射。

展望


此处描述的应用程序是用来显示和验证不同供应商的 Web 服务实现之间的一定级别的互操作性。Web 服务互操作性组织(Web Services Interoperability organization,WS-I)的发起目的是提供保证不同供应商的 web 服务间互操作性的文档标准和测试工具。WS-I 的具体工作产品之一就是基本概要文件(Basic Profile)。基本概要文件把特定规范的范围缩小到一组最适合帮助实现互操作性的合理规则和方针。第一个基本概要文件的草案最近已发布了。订单应用程序将来的一项工作是严格遵守这个概要文件中展示的规则。但它仍有可能不局限于已定义的规则以表明可以实现更高级别的互操作性。最重要的是,WS-I 基本概要文件强制使用 SOAP 请求和响应消息的文字编码。本文所示的应用程序也使用 SOAP 编码。这样,您就可以学习到有关不同的实现在一起协作的边界的许多知识。此外,现在 Web 服务世界中有许多更新的标准和规范正在发展之中,如 WS-Coordination 和 WS-Transaction 规范。当各种供应商都可以提供这个演示时,这个演示将来的版本一定会包含这些更新的技术。

总结


Web 服务技术可以帮助实现异构平台和应用程序之间更高级别的自动化和集成。本文显示的应用程序虽然只是一个演示,但它显示了可以使用 web 服务技术把不同的平台和编程语言集成在现实的案例中。正如 WSDL 中描述的那样,抽象服务接口被用于创建一个抽象层,这个抽象层允许(假设)用 Java 编程语言写的客户机来访问(假设)用 C++ 实现的服务。

为了确保不同供应商提供的 Web 服务能够很好地协作,必须把规则和方针写成文档。这将使新的业务流程更容易实现,而不必再创建集成软件的其他层。Web 服务互操作性组织在这方面起到了举足轻重的作用,并且这里显示的演示应用程序可以作为包含新技术和规范的基础,随着时间的推移,它们可以发展成为产品。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=20726
ArticleTitle=Web 服务互操作性,第 1 部分
publish-date=03012003