级别: 初级 娄丽军, 软件部售前工程师, IBM
2005 年 6 月 01 日 在第一部分中,我们了解了Server Foundation的基本概念和建模技巧,在这一部分中,我们将介绍动态建模和与JMS交互这两个话题。
1 动态建模
所谓动态建模是指在建模之初,某些流程特征是未知的,需要在流程实例运行起来之后,在运行时(Runtime)获知并设定这些特征,因此无法在建模时静态地描述流程。
Server Foundation提供了对动态建模功能的很好支持,在Server Foundation中动态建模体现在如下几个方面:
- 动态超时
- 动态人员分派
- 动态描述流程特征
- 动态激活子流程
下面,我们分别介绍如何实现这几方面的动态配置。
1.1 动态超时的设定
在Server Foundation中,有几个活动(Activity)会导致流程实例的暂时停止,如:
:等待一个给定时间间隔
:阻塞流程实例,等待一个事件到达,或超时时间到
:人机交互操作
这些Activity有可能会持续很长时间,从而导致流程实例的长时间阻塞。以Staff活动为例,当我们把某项工作分派给某个人员时,如果在特定的时间之内,该人员始终没有完成该任务,我们该如何处理这种情况?最好的处理方式是设定这些活动的Expiration时间属性,在特定的时间之后,我们可以进行超时处理,例如可以给该人员发送一个警告等等。
利用Expiration时间属性,我们可以为流程中的某个环节设置超时时间,在超时时间到达时,流程继续下面的处理环节。在Server Foundation中,对于Wait, Staff, Pick(onAlarm), Invoke这四种类型的Activity,我们可以设置其超时时间。
在Server Foundation中,Expiration时间属性的设置方法如下:
对于Invoke 和 Staff Activities,通过Expiration属性栏设置,对于Wait Activity 和 OnAlarm,通过Condition属性栏设置。
其中设置选项分为四种:
- None - 没有超时时间限制
- Duration - 在特定时间间隔后超时,例如: 1000 秒
- Date - 当特定的时间和日期到达时超时,例如:09/04/2005,或者是一个表达式
- Timeout - 在某个类型的日历指定时间到达时超时,可以是WebSphere CRON 或者用户自定义的日历
我们可以利用表达式进行动态超时的设定:
- 选择Expiration类型,如:Date
- 在Condition Type中,选择表达式作为条件类型
- 在Expression中,输入自己的表达式
1.2 动态人员分派
动态人员分派是指在流程实例运行起来之后,根据某种规则动态决定某个环节上的执行人。在Server Foundation中,有四种方法动态指定人员:
- 利用流程上下文变量(Context Variable)
在流程执行过程中通过预定义的变量来指定人员,例如:流程启动者、活动拥有者等。在Server Foundation中,预定义的变量包括:wf:process.starter、wf:process.administrators、wf:process.readers、wf:activity(name).potentialOwners、wf:activity(name).readers、wf:activity(name).editors、wf:activity(name).owner等。正确的语法是%变量名%。
-
利用动态人员查询(Staff Queries):Server Foundation支持三种方式的人员定义,即基于操作系统的,基于LDAP的,基于客户化的人员注册(Custom Registry),利用客户化人员注册的方法可以动态指定某个任务项的执行人员。
- 利用流程的Custom Properties(定制属性),关于定制属性的概念,我们在第一部分已经有相应的描述。
在人员活动之前,可以利用JavaSnippet 活动,在其中编写代码来设置定制属性;然后,在人员活动的人员属性参数设定中,利用%变量名%来获得定制属性。
在利用定制属性来设定人员时,要考虑以下限制:它只支持用户 (user)和组(group)的查询;对于定制属性的设定只能通过Server Foundation的API来设定,不提供图形界面。
- 利用流程变量
在建模时,我们可以通过定义流程变量,在Staff Activity的人员属性的参数设定中指定人员。对于简单类型的变量,可以通过%VariableName\PartName%
对于复杂类型的变量,可以通过%VariableName\PartName\<XPath Statment>%来指定变量中的某个成员。相对定制属性来指定人员的方法,通过流程变量的方法不需要编程代码,但是要注意的是其成员变量必须为string类型。
1.3 动态描述流程属性
在Server Foundation中,对于流程和活动,我们都可以设定其Description(描述)属性,在流程建模时,通过灵活地设定描述属性,可以灵活地方便我们实现对流程的控制,我们可以通过流程API来获得流程和变量的描述属性。设置的方法是:设置Process或Activity的Documentation属性,对于Activity,其语法是%MessageName\PartName%;对于Process,其语法是%\PartName,由于在Process启动时,我们会指定Process Input Variable(流程输入变量),因此,不需要指定变量名。
要注意的是,对流程和变量的描述属性的字符串长度不能超过254个字符。
1.4 动态调用子流程
动态流程调用是指在建模时,我们不能把流程写死,而是在流程实例运行起来之后,动态决定流程的走向。如根据文档的某个字段的信息标示,来决定处理它的部门。
Server Foundation提供动态服务绑定的功能,来实现对子流程(严格地说,应该是另外一个流程)的动态调用,该功能也称为late-binding(后绑定),即在流程执行时,可以动态地将某个服务的Endpoint引用赋给合作伙伴连接(Partner Link)。代码示例如下:
EndpointReferenceType e = getPartnerLink<MyService>();
AttributedURI address = e.getAddress();
URI addressValue = new URI();
addressValue.setHost("localhost");
addressValue.setPath("/StockQuote/services/StockQuote");
addressValue.setPort(9080);
addressValue.setScheme("http");
address.setValue(addressValue);
ServiceNameType serviceName = e.getServiceName();
serviceName.setPortName(new NCName("StockQuote"));
serviceName.setValue(new QName("http://sample.bpe.ibm.com", "StockQuoteService"));
setPartnerLink<MyService>(e);
|
2 JMS接口
从编程角度讲,Server Foundation提供两种对外接口方式,一是通过EJB Interface来启动流程,另外一种是通过JMS接口来启动流程。而对于JMS接口来讲,又可以分为两种类型:一个是WSIF JMS 接口 (即Native JMS Provider),另外一个是Generic JMS 接口 (即Internal Engine MDB)。
2.1 通用JMS接口
在调入方向上,主要应用于利用JMS接口来启动流程的场合,这时要使用通用(Generic) JMS 接口。利用通用JMS接口时,流程引擎只支持Object Message消息类型,不支持Text Message消息类型,发送给流程引擎的消息必须被封装为ClientObjectWrapper,从流程引擎返回的消息也必须是ClientObjectWrapper对象,例如:
WSIFDefaultMessage wsifMessage = new WSIFDefaultMessage();
Customer cust = new Customer();
cust.setPart1("value1");
cust.setPart2("value2");…..
wsifMessage.setObjectPart("Customer", cust);
ClientObjectWrapper cow = new ClientObjectWrapper(wsifMessage);
|
在利用Server Foundation的JMS接口时,会用到两个重要的目标队列:BPEApiQueue和BPEIntQueue。利用通用JMS API与流程引擎打交道时,都要通过BPEApiQueue。另一个BPEIntQueue是供流程引擎内部使用的。
使用通用JMS接口的例子如下:
message.setStringProperty( "wf$porttype", "{http://www.example.com}MyProcessPortType" );
message.setStringProperty( "wf$operation", "receiveExternalData" );
message.setStringProperty( "wf$processTemplateName","MyProcess" );
WSIFDefaultMessage input= new WSIFDefaultMessage();
input.setObjectPart("part1", "string1");
input.setObjectPart("part2", "string2");
message.setObject(new ClientObjectWrapper(input));
queueSender.send(message);
|
2.2 WSIF JMS接口
在调出方向上,主要用于集成MQ/JMS类型的应用时的场合,这时要使用WSIF JMS 接口。
在通过Server Foundation集成外围应用时,我们可能会遇到这样一些难题,我们要集成的可能是MQ based的应用,尤其是这些应用可能根本不是采用Java接口开发的;或者要集成的某些应用很难提供Web Services接口,这时,我们可以采用JMS方式与这些应用集成,这也是较为常见的一种方式。在Server Foundation中,我们可以采用WSIF JMS 绑定的方式来实现上述需求。
WSIF JMS接口相对于通用JMS接口来说要复杂一些。
WSIF JMS接口支持准同步(Pseudo-Synchronous)调用、带返回的异步调用(Asynchronous Invoke with Callback)、 调用不等返回(Fire-and-forget)等几种类型。
(I)准同步(Pseudo-Synchronous)调用
为什么称其为准同步调用呢?因为严格地说它并不是一个完整的同步调用,流程引擎是使用两个交易来实现的,一个发送请求消息,另外一个接收应答消息。
适用于可中断以及不可中断的流程,在WSDL中定义输入/输出消息和JMS绑定。当采用请求/应答模式时,要求JMS Response消息必须被返回到BPEIntQueue中,因此,在发出Request消息时,要将其JMSReplyTo属性设置为BPEIntQueue。
请求消息和应答消息的格式通过WSDL消息定义来描述。
为了实现一个JMS Invoke活动,我们需要进行以下配置:
- 创建底层的JMS队列目标(Queue Destination)和队列连接工厂(Queue Connection Factory),创建对应于MQ的队列和队列管理器;
- 在流程的应用部署描述符中,添加队列目标和队列连接工厂的定义;
- 创建WSDL描述文件,其中包括对输入/输出消息和JMS绑定的定义;
- 设置Invoke活动的交易属性为"requires own",保证一个独立的交易来向队列中发送消息;
- 将JMS队列的超时属性设置为"unlimited" ,确保读取消息的应用程序能够从队列中读取到消息。
以可中断流程为例,它的调用过程如下:
1) 消息发送到定义的队列
2) 流程引擎负责将发送消息的ReplyToQ设置为BPEIntQueue
3) 流程引擎自动生成一个MessageID,并利用此ID来关联应答消息和请求消息,因此不必设置BPEL的CorrelationSet
4) 接收端应用程序接收到消息,通过getMQMessageID()获得MessageID,将此MessageID设置为应答消息的CorrelationID,并将应答消息发送回BPEIntQueue
5) 流程引擎获取应答消息,并通过CorrelationID自动关联到正确的流程实例
6) Formatter再将消息转化为流程引擎要求的格式
对于非中断流程,所不同的只是接收应答消息的队列是一个临时队列,而非永久队列。
这里有一个非常重要的知识点,就是关于消息格式转换的问题。在利用Server Foundation的WSIF JMS接口时,流程引擎处理的是一种特定的消息格式(BPC Message),而我们的外部应用处理的却是标准的JMS消息,其中包括Text Message和Object Message,在调用过程中必须要实现这两种消息格式之间的转换。幸运的是,WSAD-IE开发工具会自动为我们提供一个类来做这件事情,从而大大减少了用户的工作量。当使用WSADIE来创建WSDL描述文件时,WSADIE会自动产生FormatHandler 类, 利用Formatter来实现消息格式的转换。
(II)带返回的异步调用(Asynchronous Invoke with Callback)
体现为一个单向(one-way)的Invoke活动发送请求消息和一个Pick/Receive活动来接收应答消息。
它的调用过程如下:
1) 消息发送到定义的队列
2) 流程继续下面的执行逻辑,直到接下来的Pick活动处等待,直到接收到应答消息,这里,需要设置流程BPEL的CorrelationSet,通过CorrelationSet来关联到正确的流程实例
3) 应答消息被发送到BPEApiQueue中,因此要遵循Generic JMS接口标准
要注意的是,在这种情形下,需要采用两种不同的JMS编程接口,发送请求消息时,使用的是WSIF JMS接口,接收应答消息时使用的是通用 JMS接口。
(III) 调用不等返回
体现为一个单向(one-way)的Invoke活动,它只有一个输入消息,这是一个完全异步的工作模式。
3 与Message Broker的集成
Message Broker是IBM的ESB解决方案。我们在一些复杂项目中经常会同时使用Server Foundation和Message Broker,随之而来的就会遇到二者集成的问题,对于二者的集成,我们有多种解决方案的选择,其中包括:
- 通过MQ队列:可以通过WBI MQ适配器来实现,当消息到达队列时,驱动一个Server Foundation中的BPEL流程;反之亦然,因为适配器是双向的。
- 通过JMS接口规范:Server Foundation对外提供了JMS接口来驱动一个流程,通过使用IBM给出的Message Broker的JMS SupportPac,可以使得Message Broker能够构造一个JMS消息发送到Server Foundation的队列中。
- 通过Web Services:Message Broker提供全面的Web Services支持,在一个Message Flow中,可以调用外部的一个Web Services;反之,它的Message Flow也可以被封装为一个Web Services。而Server Foundation更是天然支持Web Services接口。
- 通过BPE API接口:在Message Broker中,可以开发客户化的Java Node,利用该节点来读取来自Server Foundation的消息;另外,通过MDB可以读取Message Broker发送到某个队列里的消息,通过BPE API来驱动Server Foundation的流程。
在MB调Server Foundation时,需要注意的是:
MB使用的是MQ的Native API,因此发送到队列中的消息格式是字符串类型的,而Server Foundation流程引擎期望的消息格式是WSIF Object类型的,同理,引擎发送到应答队列中的消息也是Object类型的,需要进行二者之间的转换。
这里给出了一个MB调用ServerFoundation流程的例子。我们通过一个客户化的MDB来实现不同消息格式之间的转换。使用MDB的好处在于:
- 通过MDB可以灵活地实现MQ Native 字符串消息到工作流引擎WSIF消息的转换;
- 可以侦听任何队列
- 可以通过BPE EJB 接口来启动流程
- 可以通过Correlation ID实现输入消息和输出消息的关联
关于作者  | |  | 娄丽军,IBM公司软件部售前工程师,1998年加入IBM公司软件部,四年来一直从事IBM通讯及业务整合中间件(WebSphere Business Integration家族)产品的技术支持工作,是软件部从事该领域技术支持时间最长的工程师之一,拥有WebSphere Business Integration相关的产品经验,这些产品包括WebSphere MQ家族的所有产品:MQSeries, MQ Integrator, MQ Workflow以及CrossWorlds等,并具有很多大型项目的支持经验,曾参与国家税务总局,人民银行清算系统、华夏银行电子联行系统、中国联通计费系统、海关与国家税务总局互连系统,以及公安部、铁道部、中信实业银行、电信等重要客户的有关项目的技术支持。 |
对本文的评价
|