注意:您应该熟悉 WebSphere Studio Application Developer Integration Edition Version 5.1.1 Web 服务开发环境、ASP .NET Web 服务,并了解构建 BPEL 流程的知识。本文还带有 BPEL 业务流程的样本代码。
由于 XML 和 Web 服务需要使用 BPEL,它迅速成为面向服务体系结构(SOA)的基础,BPEL 还提供了 WebSphere Application Server Enterprise Process Choreographer(Process Choreographer)的公开标准。这些年来,许多企业应用程序都分别在 J2EE 和.NET 平台上被独立和并行的开发和部署。这些业务应用程序都设计有细粒度的业务功能。例如,在 J2EE 中,使用实体 bean 实现信息持久性,并使用会话 bean 实现业务逻辑。这些业务应用程序同样还在有限的业务域里提供集成框架,用于后端或是遗留企业应用程序的集成。举例来说,Java Connector Architecture(JCA)和 Java Messaging Service(JMS)都是典型的 J2EE 应用程序集成框架。
随着 Web 服务的出现,后端企业应用程序通过使用 WSDL 被公开为可发现并可调用的业务服务。WSDL 为 Web 服务接口定义了服务语义,例如操作、协议绑定和消息类型等。BPEL 层在 WSDL 之上,它指定参与流程流的复合 Web 服务的行为。因此,它使业务分析人员和架构师可以定义业务流程流的逻辑,并可以使用 BPEL 来支持与 J2EE Web 服务和 .NET Web 服务的长期运行的会话。
实际上,BPEL 流程流成功与否,基本取决于每个 Web 服务的 WSDL 文档中定义的 XML 服务语义。XML schema 使 XML 与其他文件格式区别开来,XSD 是 XML schema 定义,它是综合且复杂的数据类型定义系统。简单地说,XSD 定义了 XML 文档的外型。使用 XSD 设计简单且强类型(strongly-typed)的对象是 Web 服务互操作性的基础。“改进 J2EE 技术和 .NET 间的互操作性”(本系列的第 1 部分)指出,许多 Web 服务编程人员忽视了 XSD schema 设计的重要性。换句话说,即他们使用自己喜爱的编程语言来为 Web 服务实现编写代码,并随后用供应商的工具从实现中获得 Web 服务语义。这种自底向上的方法产生了有关互操作性的问题。
.NET 和 J2EE 之间的互操作性问题通常源于 XML 命名空间和复杂数据类型,例如嵌套的复杂类型数组以及日期和时间(本系列的第 2 部分和第 3 部分)。本文中讲述的技巧将展示在 BPEL 流程集成中如何在两个平台之间安全并正确的传递嵌套数组、复杂类型和日期。但这需要您为这些复杂类型仔细设计 XSD schema。
|
要构建流程,您需要在 Windows 中安装 IBM WebSphere Studio Application Developer V5.1.1 和 Microsoft .NET Framwork 1.1。对于本文来说,这两种产品都在同一台机器上安装并运行。.NET Visual Studio 是用来构建 .NET Web 服务的集成工具,在本技巧中不予使用。
IIS 的文档根目录在缺省情况下为 C:/Inetpub/wwwroot。我将使用该目录发布 .NET Web 服务。同时,.NET Framework 1.1 安装在 C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322 目录下,且 SDK 在 C:/Program Files/Microsoft.NET/SDK/v1.1 目录下。
将以下目录添至系统 PATH 变量:
- c:/Windows/Microsoft.NET/Framework/v1.1.4322
- c:/Program Files/Microsoft.NET/SDK/v1.1/Bin
BPEL 业务流程的样本代码在下载部分。
|
设想一个购买场景,购买者通过货物代理商来执行定购请求。货物代理商拥有许多提供货源的供应商;每个参与者都是独立的订户且拥有自己的产品库存管理系统。也就是说,某个供应商可能运行 J2EE Web 服务来管理其库存而其它供应商可能会使用 .NET Web 服务来进行相同的操作。
购买流程从向不同的供应商索取产品报价开始。在购买者提交订单之前,代理商与每个供应商联系以获取相应产品的报价,且每个供应商将返回该产品的详细信息。之后购买者将浏览信息并继续进行下个定购流程。图 1 展示了两个供应商(Supplier A 和 Supplier B)报价请求的流程图。购买者请求代理商提供报价,代理商将该报价请求传给各供应商,并随后将供应商提供的信息返回给购买者。
图 1.报价请求的流程图
在该图中:
- Buyer 是提出购买请求的客户端。
- Agent 是业务流程,请求供应商提供产品信息并处理购买者的订单。
- Supplier A 是 Java Web 服务,管理供应商 A 的库存。
- Supplier B 是 .NET Web 服务,管理供应商 B 的库存。
对于购买者提出的购买请求,代理流程将首先对各供应商构造产品报价请求。各供应商作出反应,提供相应的产品信息,包括价格、数量和其它产品信息。代理随后将这些产品信息返回给购买者以供浏览和定购。
在接下来的章节中,我们将构建代理流程,为 Supplier A 构建 Java Web 服务,并为 Supplier B 构建 .NET Web 服务。
|
在本系列的第 1 部分中,您将发现,常被忽视的最重要的最佳实践之一是用于 Web 服务的 XML schemas 和 WSDL 设计。编程人员通常用自己喜爱的编程语言开始构建 Web 服务,然后使用供应商工具获取 Web 服务语义并公开 WSDL。这就是所谓的自底向上的方法——它并不考虑 Web 服务是围绕消息进行的,且数据和消息类型都必须设计得很谨慎而不是简单地用工具生成。本部分将展示如何使用 WebSphere Studio Application Developer Integration Edition Version 5.1.1 为 Web 服务设计 XML schema 和 WSDL。
对于 Supplier A,库存服务必须执行至少两个操作:getQuote
用来返回库存中的产品信息,fulfillOrder
用来执行来自代理的定购请求。下文中的图 2 展示了该服务接口的流程。
图 2. Supplier A Web 服务接口图
Product
是一个 complexType
,有四个属性;每个属性都是 基本数据类型。本部分将展示如何在 WebSphere Studio Application Developer Integration Edition Version 5.1.1 中利用 XML schema 编辑器来用 XML schema 语言定义产品类型。下个部分将展示如何设计 .NET Web 服务的复合 Product complexType
来演示互操作性。
首先,创建服务项目和包,用来为所有伙伴 Web 服务和流程定义保存 XML schema 文件和 WSDL。
- 启动新的 WebSphere Studio Application Developer Integration Edition Version 5.1.1 工作区。
- 选择 File>New>Service Project。
- 指定服务项目名称为
QuoteProcessService
,并点击 Finish。 - 右键单击
QuoteProcessService
项目并选择 New>Package。 - 输入新 Java 包的名称
quote.process
并单击 Finish。
接下来,创建新的 XML 并定义 Product complexType
。
- 右键单击
quote.process
包并选择 New>XML schema。 - 输入新的 schema 名称
SupplierASchema.xsd
并单击 Finish。打开 XML schema 编辑器。 - 在 Outline 视图中,选择
SupplierASchema.xsd
。 - 在 Schema 面板中,输入
http://schema.a.supplier
作为命名空间 URI。这将作为 schema 的目标命名空间。点击 Apply。 - 在 Outline 视图中,右键单击
SupplierASchema.xsd
并单击Add Complex Type
。 - 在 Outline 视图中,选择
NewComplexType
。 - 在 Complex Type 面板中,输入复杂类型的名称:
Product
。 - 在 Outline 视图中,右键单击
Product
类型并单击Add Content Model
。 - 在 Outline 视图中,扩展
Product
。 - 右键单击 content model icon 并单击
Add Element
。添加NewElement
。 - 在 Outline 视图中,选择
NewElement
. - 在 Element 面板中,将
NewElement
重新命名为_name
并将类型设置为xsd:string
。
请注意步骤 4,schema 的目标命名空间设置为 http://schema.a.supplier
而不是 http://a.supplier/schema
。这是 JAX-RPC 行为的结果:JAX-RPC 将使用 URI 的域名部分来生成可序列化产品类的包名。为避免潜在的命名冲突,命名空间 URI 的域名部分应该尽可能的细粒度,如"改进 J2EE 技术和 .NET 间的互操作性,第 3 部分"所示。另外一个原因是如果序列化器类的包名是 supplier.a
,那么当接收到命名空间限定的产品对象({http://a.supplier/schema}Product
)时,一些客户端查询反序列化器会失败。
重复最后三个步骤,创建 Product
类型的其它元素并保存 XSD 文件。
_price: xsd:float
_qty: xsd:int
_refurbished: xsd:boolean
您可以为 Supplier A 设计 WSDL 并定义服务方法(操作)和消息绑定。在报价流程中,仅需 getQuote
操作。
首先,创建空 WSDL,并导入在之前步骤中定义的产品 schema。
- 右键单击
quote.process
包。选择 New>Other>Web Services>WSDL 并单击 Next。 - 输入新的 WDSL 名
SupplierAService.wsdl
并单击 Next。 - 在向导中,设置目标命名空间为
http://a.supplier/service/
并单击 Finish。打开 WSDL 编辑器。 - 在 Outline 视图中,右键单击 Imports 并选择 Add Child>Import。
- 在 Import 面板中,单击在位置 box 旁边的 push button。
- 浏览到
SupplierASchema.xsd
并单击 OK。
Product complexType
被导入到命名空间 http://schema.a.supplier
下的 WSDL 中。
接下来,创建消息和消息部件。对于每次 getQuote
调用,将会产生请求消息传送产品名称,以及响应消息返回来自库存的 Product
对象。
- 在 Outline 视图中,右键单击 Messages 并选择 Add Child>Message。
- 输入名称
getQuoteRequest
。 - 右键单击新创建的
getQuoteRequest
并选择 Add Child>Part。 - 将新消息部件命名为
productName
并单击 OK。
请注意,消息部件名称的缺省类型为 xsd:string
。接受该缺省类型。现在重复以上步骤。用消息部件 product 创建新的消息 getQuoteResponse
,该消息是导入的 SupplierASchema.xsd
中已定义的 Product
类型。
- 右键单击
Product
消息部件并单击 Set Type。 - 选中 Select Existing Type 单选框,并选择
xsd1:Product
,如以下图 3 所示。单击 Finish。
图 3. 指定 product 消息部件类型
接下来,定义 Port Type
,所有的服务操作都在此处被展示。在该场景中,只定义了 getQuote
操作。
- 在 Outline 视图中,右键单击
Port Types
并选择 Add Child>Port Type。 - 将新端口类型命名为
SupplierAQuotePortType
并单击 OK。 - 右键单击
SupplierAQuotePortType
并选择 Add Child>Operation。 - 输入新操作名
getQuote
并单击 OK。 - 右键单击
getQuote
操作并选择 Add Child>input。 - 右键单击 input>Set Message。
- 选择现有的
tns:getQuoteRequest
消息,该消息在步骤 4. 创建消息和消息部件中已定义,然后点击 Finish。
重复以上的三步,创建 getQuote
操作的输出并将其链接至 tns:getQuoteResponse
。
最后,您需要定义服务端口的绑定协议。
- 在 Outline 视图中,右键单击
Bindings
并选择 Add Child>Binding。 - 指定绑定细节,如以下图 4 所示。请确保选择 document/literal 作为 SOAP 绑定选项。单击 Finish。
- 右键单击
Services
并选择 Add Child>Service。 - 输入
SupplierAQuoteService
作为新服务名称并单击 OK。 - 右键单击
SupplierAQuoteService
并选择 Add Child>Port。 - 如图 5 所示,输入端口细节并单击 Finish。
图 4.指定绑定细节
图 5.指定服务端口细节
最后,您可以实现基于 SupplierAService.wsdl
和 SupplierASchema.xsd
的 Web 服务。WebSphere Studio 可以从 WSDL 生成 Skeleton Java bean Web 服务。
- 选择 File>New>Other>Web Services>Web service 并单击 Next。
- 在接下来的 Web Service 向导中,选择 Web 服务类型为 Skeleton Java bean Web Service。对其它域的保留缺省设置并单击 Next。
- 在接下来的窗口中,指定
SupplierAServiceEAR
为 Service project EAR,并指定SupplierAServiceWeb
为 Service Web 项目。这是拥有 Java Web 服务的企业项目。单击 Next。 - 浏览到创建好的
SupplierAService.wsdl
。单击 OK,单击 Next,然后单击 Finish。
检查 SupplierAServiceWeb
项目。可序列化的 Product
类由 SupplierASchema.xsd
中定义的复杂类型 Product
生成,且 Java Skeleton Web 服务也已构建。但是,这还只是有接口功能的空服务;接口操作 getQuote
的具体实现需要在 SupplierAQuoteServiceBindingImpl
中手工提供。
首先,添加构造函数至 Product
类。
清单 1.添加构造函数至 Product 类
|
接下来,添加构造函数至 SupplierAQuoteServiceBindingImpl
类,用以对库存硬编码。实际上,您可能需要公开接口方法,例如 addInventory(Product item)
用来重新储存产品。
清单 2.添加构造函数至 SupplierAQuoteServiceBindingImpl 类
|
用清单 3 中的代码替代 SupplierAQuoteServiceBindingImpl
类中的 getQuote
方法。
清单 3. 替代 getQuote 方法
|
Supplier A 的报价 Web 服务已经准备好,可以进行部署和运行了。在 BPEL 流程创建之前还不能对其进行测试。
|
对于 Supplier B 的 .NET Web 服务,schema 要更复杂一些:在其它复杂类型中再嵌套复杂类型数组。在编程语言中添加的这种复杂性并不十分奇怪,事实上是非常正常的事情。但是,它经常是导致 XML 消息序列化失败的起因。特别是,消息接受方通常不能匹配合适的 XML 序列化器类。:dateTime
也是 J2EE 和 .NET 间出现互操作性问题的常见根源。本技巧的主要目的之一,就是说明如何为消息和数据类型谨慎地设计 XML schema,以避免出现互操作性问题。
要构建 .NET Web 服务,您需要使用稍微有些不同的方法。但通常都应该首先设计 XSD schema。
假设 Supplier B 对保存它的库存产品信息有不同的需求。与 Supplier A 中列出库存产品名称不同,Supplier B 保存了产品信息清单,例如生产日期、库存日期或是重新进货日期等。
图 6.Supplier B Web 服务的接口图
在 UML 图中,Product
有 _dates
属性,该属性是 DateInfo
DateInfo
都是复杂类型。在编程语言中,UML 集合转化成为数组。但在另一个端,如何用 XML schema 或是 XSD 类型来表示
Product
和
DateInfo
之间的关系呢?此时,需要另一个复杂类型的属性
ArrayOfDateInfo
用来描述两者之间的关系。
ArrayOfDateInfo
类型有
DateInfo
类型元素的未绑定序列。因此,要正确序列化 .NET Web 服务中的
Product
对象,需要在 XML Schema 中定义三种复杂类型:
DateInfo
、
ArrayOfDateInfo
和
Product
。
和先前演示的步骤类似,首先用目标命名空间 http://schema.b.supplier
创建 SupplierBSchema.xsd
并定义复杂类型 DateInfo
,如清单 4 所示。
清单 4. 定义复杂类型 DateInfo
|
接下来,通过 SupplierBSchema.xsd
中 DateInfo
类型元素的未绑定序列来添加复杂类型 ArrayOfDateInfo
。
- 在 Outline 视图中,右键单击
SupplierBSchema.xsd
并单击 Add Complex Type。 - 将新的复杂类型重命名为
ArrayOfDateInfo
。 - 右键单击
ArrayOfDateInfo
并单击 Add Content Model。 - 右键单击 content model icon 并单击 Add element。
- 将新元素重新命名为:
DateInfo
。 - 设置用户定义的复杂类型:
SupplierBSchema:DateInfo
。 - 将
minOccurs
属性设置为零,并将maxOccurs
属性设置为 unbounded。
ArrayOfDateInfo
schema 的结果如 清单 5 所示。
清单 5. ArrayOfDateInfo schema
|
按照如定义 complexType Product
类似的步骤进行,如以下清单 6 所示。
清单 6. complexType Product
|
在 .NET Framework 1.1 中,XML Schema Definition 工具(Xsd.exe)可通过 XSD 文件生成运行时类。Xsd.exe 实用程序通过 XSD schema 中生成一组 C# 类模板。通过模板,您可以提供具体的实现并生成整个项目。然而,Xsd.exe 实用程序需要在 schema 中定义至少一个顶级元素,因此您需要定义一个全局元素:productItem
。
- 在 Outline 视图中,右键单击
SupplierBSchema.xsd
并单击 Add Global Element。 - 将该元素命名为
productItem
,并将其类型设置为SupplierBSchema:Product
。 - 保存文件。
现在,导出 SupplierBSchema.xsd
文件到一个目录,并运行该目录中的 Xsd.exe 命令,生成 C# 类型类:xsd.exe SupplierBSchema.xsd /classes
。
一组 C# 类型的类在 SupplierBSchema.cs
文件中生成。DateInfo
和 Product
类通过 http://schema.b.supplier
命名空间定义并限定。请参见 Download 部分关于类的完整资料。在设计完互操作性场景中最重要的部分后,接下来开始构建 .NET Web 服务实现。在 构建代理流程 部分中,SupplierASchema.xsd
和 SupplierBSchema.xsd
同样是构建 BPEL 流程的起始点。
要构建 Supplier B 的 .NET Web 服务,您可以使用 .NET Visual Studio 构建 .NET 部件,或者在 .asmx
文件中,简单地编写 C# 代码并将类型类封装在 SupplierBSchema.cs
文件中,此处将使用后一种方法。
- 复制
SupplierBSchema.cs
文件至C:/Inetpub/wwwroot/SupplierB/
目录并重命名为SupplierBQuoteService.asmx
,在编辑器中打开。 - 注释掉以下行:
[System.Xml.Serialization.XmlRootAttribute("productItem", Namespace="http://schema.b.supplier", IsNullable=false)]
. - 将构造函数添加至
Product
和DateInfo
类中,并进行初始化。在 .NET 中,序列化类也需要缺省构造函数。public DateInfo() {} public DateInfo(DateTime date, string desc) { _date = date; _desc = desc; }
andpublic Product() {} public Product(string name, int qty, float price, DateInfo[] dates) { _name = name; _qty = qty; _price = price; _dates = dates; }
- 添加 Web 服务类
SupplierBQuoteService
并在命名空间 http://b.supplier/service 下将getQuote
方法公开为 document/literal Web 服务方法。在 .NET 中,document/literal 是缺省绑定样式:[WebService(Namespace="http://b.supplier/service")] public class SupplierBQuoteService { private static Hashtable fCurrentInventory = null; private static Hashtable getCurrentInventory() { return fCurrentInventory; } public SupplierBQuoteService() { fCurrentInventory = new Hashtable(); Product item1 = new Product("IBM ThinkPad T40", 200, 1399.99f, new DateInfo[] {new DateInfo(DateTime.Now, "Manufacture Date"), new DateInfo(DateTime.Now.AddYears(3), "Expiry Date")}); Product item2 = new Product("Dell Inspiron 4000", 200, 899.99f, new DateInfo[] {new DateInfo(DateTime.Now, "Manufacture Date"), new DateInfo(DateTime.Now.AddYears(5), "Expiry Date")}); Product item3 = new Product("Toshiba Satellite 2210X", 200, 599.99f, new DateInfo[] {new DateInfo(DateTime.Now, "Manufacture Date"), new DateInfo(DateTime.Now.AddYears(10), "Expiry Date")}); getCurrentInventory().Add("IBM ThinkPad T40", item1); getCurrentInventory().Add("Dell Inspiron 4000", item2); getCurrentInventory().Add("Toshiba Satellite 2210X", item3); } [WebMethod] public Product getQuote(string quoteItemName) { string item = quoteItemName; if (!getCurrentInventory().ContainsKey(item)) return new Product(item, 0, 0, new DateInfo[] {new DateInfo(DateTime.Now, "Manufacture Date"), new DateInfo(DateTime.Now, "Expiry Date")}); else return (Product)(getCurrentInventory()[item]); } }
完成的 SupplierBQuoteService.asmx
文件包含在 Download 部分中。
现在您可以在浏览器上测试 Supplier B Web 服务的 getQuote
方法。
- 在浏览器上,输入以下 URL:http://localhost/SupplierB/getQuoteServiceImpl.asmx。
- 只返回 Web 服务方法
getQuote
。单击getQuote
方法。 - 在文本框中输入 IBM ThinkPad T40 并单击 Invoke。
将返回 IBM ThinkPad T40 产品信息,如以下 图 7 所示。
图 7. .NET Web 服务的 getQuote 方法的测试结果
请注意 Manufacture Date 和 Expiry Date 是如何显示的。
在浏览器中输入 URL: http://localhost/SupplierB/SupplierBQuoteService.asmx?wsdl。浏览器将显示 .NET Web 服务的 WSDL 文档。虽然该文档由 .NET WSDL 引擎生成,但日期类型 schema 和命名空间直接来自于前面步骤中设计的 SupplierBSchema.xsd
。在服务项目中,将 WSDL 导入至 quote.process
包。
|
在本部分中,您将定义代理 Quote Process
接口。该流程的输出数据类型由 Supplier A 和 Supplier B 的产品报价结果组合而成,如图 8 中的 UML 所示。
图 8.Quote Process 的接口图
您需要定义复杂类型,通过输入两个分别来自 SupplierASchema.xsd
和 SupplierBSchema.xsd
的 Product
复杂类型来组成最后的报价信息。
首先,创建 QuoteProcess.xsd
schema 文件并输入 SupplierASchema.xsd
和 SupplierBSchema.xsd
:
- 右键单击
quote.process
包并选择 New>XML Schema。 - 输入新 schema 名称:
QuoteProcessSchema.xsd
并单击 Finish。打开模式编辑器。 - 在 Outline 视图中,选择
QuoteProcessSchema.xsd
。 - 在 Outline 视图中,右键单击
QuoteProcessSchema.xsd
并单击 Add Import。 - 在 Outline 视图中,扩展 + sign 并单击 import icon。
- 在 Import 面板中,浏览到
SupplierASchema.xsd
文件并单击 Finish。
重复步骤 4 至 6,并导入 SupplierBSchema.xsd
文件。
接下来,创建 ProductQuotes
复杂类型:
- 在 Outline 视图中,右键单击
QuoteProcess.xsd
并单击 Add Complex Type。 - 在 Complex Type 窗口中,将新的复杂类型命名为
ProductQuotes
。 - 在 Outline 视图中,右键单击
ProductQuotes
并单击 Add Content Model. - 在 Outline 视图中,右键单击 icon: 并单击 Add Element。
- 在 Element 窗口,将新元素命名为
SupplierAQuote
。 - 将
SupplierAQuote
元素设置为用户定义的复杂类型SupplierASchema:Product
。
重复最后三个步骤,添加另一元素 SupplierBQuote
,并将其设置为用户定义的复杂类型 SupplierBSchema:Product
。
在 Source 视图中,作为结果产生的 ProductQuotes
schema 如清单 7 所示。
清单 7.ProductQuotes schema
|
BPEL 流程将作为 Web 服务进行部署和运行。接下来的步骤将创建流程接口 WSDL 并导入 QuoteProcessSchema.xsd
。
创建流程接口 WSDL。
- 在
quote.process
包中,创建空 WSDL 且命名为quoteProcessClient.wsdl
。在向导中,将目标命名空间设置为http://quote.process/quoteProcessClient/
。 - 在 Outline 视图中打开 WSDL 编辑器后,右键单击 Imports 并输入
QuoteProcessSchema.xsd
文件。
接下来,定义流程入站和出站消息。
- 在 Outline 视图中,右键单击 Messages 并选择 Add Child>Message。
- 将新消息名设置为
getQuotesRequest
并单击 OK。 - 在 Outline 视图中,右键单击
getQuotesRequest
并选择 Add Child>Part。 - 输入部件名
productName
。其缺省类型为xsd:string
。
重复上述步骤,创建新消息 getQuotesResponse
,添加消息部件 quotes,并为 quotes 设置类型。
- 在 Outline 视图中,右键单击 quotes 并单击
Set Type
。 - 将类型指定为
xsd1:ProductQuotes
并单击 Finish,如以下图 9 所示。
图 9.指定 quotes 类型
接下来,为流程添加 Port Types
以及操作并定义输入和输出。
- 在 Outline 视图中,右键单击
Port Types
并选择 Add Child>Port Type。 - 添加新
Port Type
,名为QuotesProcessPortType
。 - 右键单击
QuotesProcessPortType
并选择 Add Child>Operation。 - 将操作命名为
getQuotes
。 - 右键单击
getQuotes
操作,选择 Add Child>input 并右键单击 input。 - 单击
Set Message
并选择 Select an existing message 单选框。 - 将输入消息设置为
tns:getQuotesRequest
并单击 Finish。
重复最后三个步骤,添加输出,设置消息为 tns:getQuotesResponse
,并保存 quoteProcessClient.wsdl
。
现在,构建业务流程流。首先,创建空流程定义文件。
- 右键单击
QuoteProcessService
中的quote.process
包,并选择 New>Business Process。 - 将业务流程命名为
QuotesProcess
并单击 Next。 - 选择 Sequence-based BPEL Process 并单击 Finish。
BPEL 流程通过名为 <partnerLink> 的简单概念调用 Web 服务。<partnerLink> 是 Web 服务 WSDL 文件定义的操作的 BPEL 抽象。伙伴链接读取外部 Web 服务的 portType
,该外部 Web 服务由 WSDL 定义。并允许 portType
中的实际操作和调用活动相关联。每个伙伴链接都与逻辑工作角色相关联。
在该流程中,有三个伙伴:Agent、Supplier A 和 Supplier B。每个伙伴服务的接口都在其 WSDL 文件中进行描述:分别为 quoteProcessClient.wsdl
、SupplierAService.wsdl
和 SupplierBService.wsdl
。
- 双击
quotesProcess.bpel
文件并在编辑器中打开,删除缺省伙伴链接。 - 将
quoteProcessClient.wsdl
拖放至编辑器并接受缺省值。 - 在编辑器中,选择
QuotesProcessPortType
伙伴链接。在 details 区域,单击 Implementation 选项卡,并单击 role switch icon,将QuotesProcessPortTypeRole
设置为流程角色名。 - 类似地,将
SupplierAService.wsdl
和SupplierBService.wsdl
文件拖放至编辑器;接受缺省值。
接下来,定义流程变量。该流程状态信息流受流程变量管理。在 WebSphere Studio Application Developer Integrated Edition V5.1.1 中,创建的所有流程变量都是全局变量。因此,你可以从任何代码块访问流程变量。各伙伴链接都有输入和输出变量,因此三对变量需要在 quotesProcess
中定义。
- 在 BPEL 编辑器中,删除缺省的
InputVariable
。 - 单击 plus icon 并添加新的
Input
变量。 - 在 Details 区域,单击 Message 选项卡,浏览到
quoteProcessClient.wsdl
文件并将其链接至getQuotesRequest
消息。
重复相同的步骤创建以下流程变量。将其链接至各自的消息,如以下表格所示:
Variable | Message | WSDL |
Output | getQuotesResponse | quoteProcessClient.wsdl |
SupplierAQuoteReq | getQuoteRequest | SupplierAService.wsdl |
SupplierAQuoteRes | getQuoteResponse | SupplierAService.wsdl |
SupplierBQuoteReq | getQuoteSoapIn | SupplierBService.wsdl |
SupplierBQuoteRes | getQuoteSoapOut | SupplierBService.wsdl |
接下来,执行 Receive
和 Reply
活动。Receive
活动接收 Web 服务请求,该请求启动报价流程。通过调用 Web 服务,Reply
活动将发送报价响应给调用者。
- 单击
Receive
活动。 - 在 Details 区域,单击 Implementation 选项卡并将
PartnerLink
设置为QuotesProcessPortType
。设置 Operation 为getQuotes
并将 Request 设置为Input
。 - 类似地,对于
Reply
活动,将PartnerLink
设置为QuotesProcessPortType
。将Operation
设置为getQuotes
并将Response
设置为Output
。
Flow
活动是一组以并行方式运行的活动,而 Sequence
是一组以串行方式运行的活动。在代理报价流程中,您希望同时调用 Supplier A 和 Supplier B 的 Web 服务。因此,创建 Flow
活动,两个序列活动将并行运行,且每个 Sequence
活动都准备好调用,调用供应方的 Web 服务,并处理结果。在 BPEL 中,Invoke
用来执行在外部实现的业务逻辑。
- 从 Palette 选择
Flow
活动并放到编辑器中,位于Receive
和Reply
活动之间。 - 从 Palette 选择
Sequence
活动并放至Flow
活动中。将Sequence
活动命名为QuoteSupplierA
。 - 准备调用 Supplier A Web 服务所需的变量。在 Palette 上,选择
Assign
活动并放到QuoteSupplierA
序列活动中。命名为Init
。 - 在 Implementation 细节区域,复制
Input
变量getQuotesRequest
消息的输入部分,并粘贴到SupplierAQuoteReq
变量getQuoteRequest
的productName
。 - 将
Invoke
拖放至QuoteSupplierA
序列活动中,并至于Init
活动下。将其重命名为getQuote
。 - 在 Implementation 细节区域,将
getQuote
活动设置为伙伴链接SupplierAQuotePortType
,将 Operation 设置为getQuote
并将 Request 消息设置为SupplierAQuoteReq
,并将 Response 消息设置为SupplierAQuoteRes
。
接下来,为 Supplier B 创建 Invoke
活动。要调用 Supplier B Web 服务,您需要使用流程变量 getSupplierBQuoteReq
的初始化 Java 片断。您也可以使用 Java 片断来执行简单的业务逻辑,而无需调用外部 Web 服务。
- 将
Sequence
活动放到Flow
活动中。并命名为QuoteSupplierB
。QuoteSupplierB
是QuoteSupplierA
的并行活动。 - 将 Java 片断拖至
QuoteSupplierB
活动中,并重新命名为Init
。 - 在 Init Java 片断的 Implementation 细节区域中,复制并粘贴以下代码:
supplier.b.service.GetQuoteElement newValue = new supplier.b.service.GetQuoteElement(); newValue.setQuoteItemName(getInput(true).getInput()); getSupplierBQuoteReq(true).setParameters(newValue);
- 将
Invoke
活动放至QuoteSupplierB
中的Init
下。重新命名为getQuote
。 - 将
getQuote
活动设置为伙伴连接SupplierBQuoteServiceSoap
,将 Operation 设置为getQuote
,将 Request 消息设置为SupplierBQuoteReq
,并将 Response 消息设置为SupplierBQuoteRes
。 - 现在,在
Flow
活动之后,但在Reply
活动准备输出前,立即放置 Java 片断。将其命名为preReply
。 - 复制并粘贴以下 Implementation 细节区域中的代码片断作为
preReply
Java 片断。ProductQuotes newValue = new ProductQuotes(); newValue.setSupplierBQuote(getSupplierBQuoteRes(true). getParameters().getGetQuoteResult()); newValue.setSupplierAQuote(getSupplierAQuoteRes(true).getProduct()); getOutput(true).setOutput(newValue);
现在,您已经完成了图 10 中定义的报价流程。
图 10. 代理 Quotes 流程
保存 BPEL 文件并生成部署代码。
- 右键单击
quotesProcess.bpel
文件并选择 Enterprise Services>Generate 部署代码。 - 在 Generate BPEL Deploy Code 向导中,单击
quotesProcessPortType
接口。 - 选择 SOAP/HTTP 作为绑定并选择 IBM Web Service。单击 OK。
代理流程作为 SOAP/HTTP Web 服务来部署。QuotesProcessServiceWeb
项目中的 WSDL 和 XSD 文件公开自身接口供任何客户端调用流程。
图 11. 代理流程的 WSDL 和 XSD 文件
|
在本部分,您可以使用图 11 中的流程 WSDL 和 XSD 文件,生成在业务场景中由购买者调用的 Java 客户端代理,如图 1 所示。
首先,创建 Java 项目 QuoteProcessTestClient
并从 QuoteProcessServiceWeb
复制 WSDL 以及三份 XSD 文件至测试客户端项目。
接下来,从 WSDL 生成 JAX-RPC 客户端代理。
- 右键单击
QuotesProcess_QuotesProcessPortType_HTTP.wsdl
文件并选择 Enterprise Services>Generate Service Proxy。 - 在向导中,选择 Java API for XML-based RPC (JAX-RPC) 作为代理类型,单击 Next 并单击 Next 继续。
- 请确保选择 Java 作为
Client
类型以及选择QuoteProcessTestClient
作为客户端项目。单击 Next。 - 在接下来的页面中,接受所有的缺省选项并单击 Finish。
JAX-RPC 代理的类设置已经生成,如图 12 所示。testclient
包中的 Buyer.java
将在下个章节进行介绍。
图 12.代理流程的 JAX-RPC 代理
|
要测试代理流程,首先要创建测试服务器并在其上部署 Supplier A Web 服务和代理流程。
- 切换到 Server 视图。创建新的集成测试服务器,配置并将其命名为
TestServer
。 - 在 Servers 窗口,右键单击
TestServer
。单击 Add 和 Remove Projects。 - 在
TestServer
上添加两个项目:SupplierAServiceEAR
和QuoteProcessServiceEAR
。单击 Finish。 - 右键单击
TestServer
并单击 Start。
接下来,实现描述 Buyer 的主要类,通过 JAX-RPC 代理来调用代理流程。
- 创建新的 Java 包。右键单击
QuoteProcessTestClient
,选择 New>Package,为新 Java 包命名为testclient
。单击 Finish。 - 创建 Buyer 类,将其命名为
Buyer
。单击 Finish。将打开Buyer
类。 - 将清单 8 中的代码复制并粘贴至
Buyer
类编辑器并保存 Java 文件。
清单 8.Buyer 类
|
最后,运行 Buyer
类获取 Supplier A 和 Supplier B 提供的关于 IBM ThinkPad T40 的产品信息。
- 选择 Package Explorer 中的
Buyer
类。 - 在顶端的菜单中,选择 Run>Run As>Java Application。如果顺利完成,结果将出现在控制台中,如以下的 图 13 所示。
图 13. 报价结果
对比图 13 和图 7 中的单元测试结果,并观察两种情况下如何描述日期信息数组。
在代理流程及其客户端代理类中,xsd:dateTime
被映射至 java.util.Calendar
,但是最好能呈现给购买者的是简洁的 java.util.Dates
结果而不是包含大量多余信息的 java.util.Calendar
。如果客户端需要 java.util.Dates
,那么需要对其进行简单转换,如清单 8 所示。
|
以下是针对 J2EE 和 .NET 开发 BPEL 流程的一些其他技巧:
- WebSphere Studio Application Developer Integration Edition Version 5.1.1 提供了强大的可视流程调试器,可以在 BPEL 流程级别上逐步调试代码。
- 对于在 Web 服务中来去的 SOAP 消息,你需要对其进行截取并研究,特别是 .NET Web 服务中的 SOAP 消息。可用的跟踪工具有很多。WebSphere 提供实体类
com.ibm.ws.webservice.engine.utils.tcpmon
用以嗅探两点之间的 HTTP 通信。您可以随意选择您熟悉的跟踪工具。 - 在大多数情况下,开启服务器跟踪查找异常的根源是十分必要的。
|
通过 BPEL 进行业务流程集成的成败关键在于参与该流程的 Web 服务的内在互操作性。文中的技巧着重强调了在设计消息模式时必须十分谨慎,且该技巧还演示了无论是哪种平台(J2EE 或是 .NET),简单 Web 服务和复杂 Web 服务都可以成功地参与业务流程。WebSphere Studio Application Developer Integrated Edition V5.1.1 提供了强大的 BPEL 流程开发环境、方便的 XML Schema 和 WSDL 设计工具。
|
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
Code for sample BPEL business process (Purchasing) | ws-solution.zip | 153 KB | FTP |
关于下载方法的信息 | Get Adobe® Reader® |
|
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
- 学习相关的 WSDL 和 SOAP 规范以及其它标准:
- 查阅该系列的其他文章:
- Peter Brittenham 编写的 了解 WS-I 测试工具 提供了对 WS-I Test Tools 体系结构和功能的概述。
- 参阅 Peter Brittenham 编写的教程 结合 Java 技术使用 WS-I 测试工具,介绍了如何按部就班地使用 Java 版本的 WS-I 测试工具
- 想了解更多吗?developerWorks Web 服务和 SOA 专区有大量信息丰富的文章和有关开发 Web 服务应用程序的入门级、中级和高级教程
- 访问 快速启用 Web 服务上关于 Web 服务的知识、工具和技术,同时还提供最新的基于 Java 的软件开发工具和 IBM 的中间件(试用版),另外还有在线教程、文章以及在线技术论坛。