您的位置:首页 > 其它

第 4 部分: 在业务流程中融合 J2EE 和 .NET 技术

2006-04-25 00:42 465 查看
注意:您应该熟悉 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 服务。








回页首
创建 Supplier A 的 Java 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
来演示互操作性。

步骤 1.创建服务项目

首先,创建服务项目和包,用来为所有伙伴 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

步骤 2.创建新的 XML

接下来,创建新的 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


步骤 3. 创建 Supplier A 的 WSDL

您可以为 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 中。

步骤 4. 创建消息和消息部件

接下来,创建消息和消息部件。对于每次
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 消息部件类型



步骤 5. 定义 Port Type

接下来,定义
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


步骤 6. 定义绑定协议

最后,您需要定义服务端口的绑定协议。

在 Outline 视图中,右键单击
Bindings
并选择 Add Child>Binding

指定绑定细节,如以下图 4 所示。请确保选择 document/literal 作为 SOAP 绑定选项。单击 Finish

右键单击
Services
并选择 Add Child>Service

输入
SupplierAQuoteService
作为新服务名称并单击 OK

右键单击
SupplierAQuoteService
并选择 Add Child>Port

图 5 所示,输入端口细节并单击 Finish

图 4.指定绑定细节



图 5.指定服务端口细节



步骤 7. 实现 Web 服务

最后,您可以实现基于
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 类

public Product(String name, int qty, float price, boolean isRefurbished) {
this.set_name(name);
this.set_qty(qty);
this.set_price(price);
this.set_refurbished(isRefurbished);
}

接下来,添加构造函数至
SupplierAQuoteServiceBindingImpl
类,用以对库存硬编码。实际上,您可能需要公开接口方法,例如
addInventory(Product item)
用来重新储存产品。

清单 2.添加构造函数至 SupplierAQuoteServiceBindingImpl 类

private static Hashtable fCurrentInventory = new Hashtable();
public SupplierAQuoteServiceBindingImpl() {
fCurrentInventory.put(
"IBM ThinkPad T40",
new Product("IBM ThinkPad T40", 200, 1499.99f, false));
fCurrentInventory.put(
"Dell Inspiron 4000",
new Product("Dell Inspiron 4000", 100, 999.99f, true));
fCurrentInventory.put(
"Toshiba Satellite 2210X",
new Product("Toshiba Satellite 2210X", 300, 599.99f, false));
}

清单 3 中的代码替代
SupplierAQuoteServiceBindingImpl
类中的
getQuote
方法。

清单 3. 替代 getQuote 方法

public Product getQuote(java.lang.String productName)
throws java.rmi.RemoteException {
if (fCurrentInventory.containsKey(productName))
return (Product) fCurrentInventory.get(productName);
else
return new Product(productName, 0, 0f, false);
}

Supplier A 的报价 Web 服务已经准备好,可以进行部署和运行了。在 BPEL 流程创建之前还不能对其进行测试。








回页首
创建 Supplier B 的 .NET Web 服务

对于 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

<complexType name="DateInfo">
<sequence>
<element name="_date" type="dateTime"/>
<element name="_desc" type="string"/>
</sequence>
</complexType>

接下来,通过
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 name="ArrayOfDateInfo">
<sequence>
<element maxOccurs="unbounded" minOccurs="0"
name="_dateInfo" type="SupplierBSchema:DateInfo"/>
</sequence>
</complexType>

按照如定义
complexType Product
类似的步骤进行,如以下清单 6 所示。

清单 6. complexType Product

<complexType name="Product">
<sequence>
<element name="_name" type="string"/>
<element name="_qty" type="int"/>
<element name="_price" type="float"/>
<element name="_dates" type="SupplierBSchema:ArrayOfDateInfo"/>
</sequence>
</complexType>

在 .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;
}

and
public 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
复杂类型来组成最后的报价信息。

步骤 1.定义复杂类型

首先,创建
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
文件。

步骤 2.创建复杂类型

接下来,创建
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

<?xml version="1.0"?>
<schema targetNamespace="http://www.ibm.com"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:QuoteProcessSchema="http://www.ibm.com"
xmlns:SupplierASchema="http://schema.a.supplier"  xmlns:SupplierBSchema=
"http://schema.b.supplier">
<import namespace="http://schema.b.supplier" schemaLocation=
"SupplierBSchema.xsd"/><import schemaLocation=
"SupplierASchema.xsd" namespace="http://schema.a.supplier"/>
<complexType name="ProductQuotes">
<sequence>
<element name="SupplierBQuote" type="SupplierBSchema:Product"/>
<element name="SupplierAQuote" type="SupplierASchema:Product"/>
</sequence>
</complexType>
</schema>

BPEL 流程将作为 Web 服务进行部署和运行。接下来的步骤将创建流程接口 WSDL 并导入
QuoteProcessSchema.xsd


步骤 3.创建流程接口 WSDL

创建流程接口 WSDL。

quote.process
包中,创建空 WSDL 且命名为
quoteProcessClient.wsdl
。在向导中,将目标命名空间设置为
http://quote.process/quoteProcessClient/


在 Outline 视图中打开 WSDL 编辑器后,右键单击 Imports 并输入
QuoteProcessSchema.xsd
文件。

步骤 4.定义流程入站和出站消息

接下来,定义流程入站和出站消息。

在 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 类型



步骤 5. 添加端口类型

接下来,为流程添加
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


步骤 6. 构建业务流程流

现在,构建业务流程流。首先,创建空流程定义文件。

右键单击
QuoteProcessService
中的
quote.process
包,并选择 New>Business Process

将业务流程命名为
QuotesProcess
并单击 Next

选择 Sequence-based BPEL Process 并单击 Finish

步骤 7. 创建伙伴链接

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
文件拖放至编辑器;接受缺省值。

步骤 8. 定义流程变量

接下来,定义流程变量。该流程状态信息流受流程变量管理。在 WebSphere Studio Application Developer Integrated Edition V5.1.1 中,创建的所有流程变量都是全局变量。因此,你可以从任何代码块访问流程变量。各伙伴链接都有输入和输出变量,因此三对变量需要在
quotesProcess
中定义。

在 BPEL 编辑器中,删除缺省的
InputVariable


单击 plus icon 并添加新的
Input
变量。

在 Details 区域,单击 Message 选项卡,浏览到
quoteProcessClient.wsdl
文件并将其链接至
getQuotesRequest
消息。

重复相同的步骤创建以下流程变量。将其链接至各自的消息,如以下表格所示:

VariableMessageWSDL
OutputgetQuotesResponsequoteProcessClient.wsdl
SupplierAQuoteReqgetQuoteRequestSupplierAService.wsdl
SupplierAQuoteResgetQuoteResponseSupplierAService.wsdl
SupplierBQuoteReqgetQuoteSoapInSupplierBService.wsdl
SupplierBQuoteResgetQuoteSoapOutSupplierBService.wsdl
步骤 9. 执行 Receive 和 Reply 活动

接下来,执行
Receive
Reply
活动。
Receive
活动接收 Web 服务请求,该请求启动报价流程。通过调用 Web 服务,
Reply
活动将发送报价响应给调用者。

单击
Receive
活动。

在 Details 区域,单击 Implementation 选项卡并将
PartnerLink
设置为
QuotesProcessPortType
。设置 Operation 为
getQuotes
并将 Request 设置为
Input


类似地,对于
Reply
活动,将
PartnerLink
设置为
QuotesProcessPortType
。将
Operation
设置为
getQuotes
并将
Response
设置为
Output


步骤 10. 创建 Flow 和 Sequence 活动

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


步骤 11. 创建 Invoke 活动

接下来,为 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 流程



步骤 12. 生成部署代码

保存 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 文件










回页首
创建 Java 客户端代理与代理流程连接

在本部分,您可以使用图 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
。单击 AddRemove 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 类

package testclient;

import java.util.Calendar;
import java.util.Date;
import process.quote.QuotesProcessPortTypeProxy;
import supplier.b.schema.DateInfo;
import com.ibm.www.ProductQuotes;

public class Buyer {
public static void main(String[] args) {
String product = "IBM ThinkPad T40";
QuotesProcessPortTypeProxy aProxy = new QuotesProcessPortTypeProxy();
try {
ProductQuotes result = aProxy.getQuotes(product);
supplier.a.schema.Product quoteA = result.getSupplierAQuote();
supplier.b.schema.Product quoteB = result.getSupplierBQuote();

System.out.println("Quotes for product: " + product);
System.out.println("/tSupplier A: ");
System.out.println("/t/tQuantity: " + quoteA.get_qty());
System.out.println("/t/tPrice: " + quoteA.get_price());
System.out.println(
"/t/tIs refurbished: " + quoteA.is_refurbished());

System.out.println("/tSupplier B: ");
System.out.println("/t/tQuantity: " + quoteB.get_qty());
System.out.println("/t/tPrice: " + quoteB.get_price());

DateInfo[] dates = quoteB.get_dates().get_dateInfo();
for (int i = 0; i < dates.length; i++) {
Calendar cal = dates[i].get_date();
Date date = cal.getTime();

System.out.println("/t/t" + dates[i].get_desc() + ": " + date);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

最后,运行
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.zip153 KBFTP


关于下载方法的信息

Get Adobe® Reader®







回页首
参考资料

您可以参阅本文在 developerWorks 全球站点上的 英文原文

学习相关的 WSDL 和 SOAP 规范以及其它标准:

W3C WSDL 1.1 规范

W3C SOAP 1.1 规范

WS-I Basic Profile 1.1

查阅该系列的其他文章:

第 1 部分:WSDL、RPC/encoded 方式 和 WS-I 一致性

第 2 部分:管理集合、数组,乃至原始数据类型

第 3 部分:处理命名空间

Peter Brittenham 编写的 了解 WS-I 测试工具 提供了对 WS-I Test Tools 体系结构和功能的概述。

参阅 Peter Brittenham 编写的教程 结合 Java 技术使用 WS-I 测试工具,介绍了如何按部就班地使用 Java 版本的 WS-I 测试工具

想了解更多吗?developerWorks Web 服务和 SOA 专区有大量信息丰富的文章和有关开发 Web 服务应用程序的入门级、中级和高级教程

访问 快速启用 Web 服务上关于 Web 服务的知识、工具和技术,同时还提供最新的基于 Java 的软件开发工具和 IBM 的中间件(试用版),另外还有在线教程、文章以及在线技术论坛。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐