级别: 中级 刘 昕鹏 (xinpengl@cn.ibm.com), 软件工程师, IBM 薛 亮 (xueliang@cn.ibm.com), 资深软件工程师,
IBM
高 希斌 (xibingao@hotmail.com), IBM CSDL实习生, 中科院
2007 年 7 月 05 日 本文主要向读者介绍了在IBM WebSphere Integration Developer中使用SCA的Web服务绑定在外部Web Service和标准SCA组件之间传递SOAP Header的基本配置方法和注意事项。本文所解决的技术问题主要应用在使用企业服务总线的SOA业务场景中,尤其是使用了IBM的WESB服务器对标准Web Service请求、应答做基于SOAP Header的中介流转的这一技术环节中。
1. 引言
随着SCA (Service Component Architecture)规范的广泛推广, SCA编程模型和IBM 支持SCA的产品系列如WebSphere Process Service(WPS),IBM WebSphere Enterprise Service Bus(WESB)越来越多的应用于实际的大型IT生产环境和业务集成中。SCA组件对业务数据进行操作,并使用统一的数据格式——SDO(Service Data Object)来表达业务数据,并在SCA组件之间进行传递。
但是在许多实际项目运营过程中,搭建单纯的符合SCA规范的IT架构一般难以实现,基于SOA可重用理念,很多情况下需要实现SCA组件和外部非SCA模块之间的集成,而这些模块往往是基于已有的J2EE平台实现的EJB、JMS终端或者能够接受SOAP消息的Web Service。IBM WebSphere Integration Developer(WID)在集成图(Assembly Diagram)上提供了SCA导入组件来引用外部的各种类型的服务组件,以及SCA导出组件来将内部的SCA组件暴露为可以和外部多种模块交互的服务接口,而所有这些交互类型都通过导入、导出组件上的绑定(Binding)类型来指定。截止到2007年初IBM发布的WID V6.0.2,用户可以指定的绑定类型包括:
- SCA 绑定
- Web服务绑定
- 无状态Session Bean绑定
- 消息绑定,包括JMS 绑定,MQ 绑定,MQ JMS 绑定
本文侧重于SCA导入、导出组件中对Web Service Binding的应用。该绑定能够在标准SOAP消息和SDO之间进行格式转换。SDO在进入SCA世界后被包装了相关的SCA协议头而成为在SCA组件之间传递的SMO(Service Message Object)。理论上来讲,SMO应该有完整的元数据格式以映射到SOAP的Schema,其中除了用于传递功能性调用的SOAP Body外,用于携带QoS以及非功能需求的SOAP Header也是重要的组成部分。WID的实现也确实做到了这一点,但是笔者在组内的项目中经历了一番挫折才认识到WPS运行时环境对实现SOAP Header在SOAP和SMO之间传递的特殊配置要求。本文就是想共享这方面的经验,希望无论是从事实际项目开发的软件工程师,还是在实验室中从事相关课题技术探索的研究人员能够从中获益,节省宝贵的研发时间。
2. 一个典型场景
笔者通过对所从事的项目进行技术抽象,得出以下的典型场景:
图1 一个典型的使用SCA中间模块基于SOAP Header进行服务选取的场景
如图1所示,假设一个Web服务请求客户端需要请求两个Web服务:Echo Web服务1和Echo Web 服务2。这两个Web服务虽然都提供了回应请求字符串的功能,但是他们的接口描述不同。该场景希望对服务请求进行中介,并选取了SCA中介模块来实现服务请求的路由、请求消息转换,面向服务请求客户端屏蔽中介细节,提供统一的服务调用界面。并且,服务请求客户端可以通过在Web服务请求的SOAP Header中嵌入路由信息,来标明每次请求所希望调用的远程Web服务。
该场景虽然只是一个技术抽象,但是可以被赋予多种IT内涵和业务语义,比较典型的有:
- 基于WS-Policy服务非功能属性的服务匹配和选取。在这种情况下,每个被选取的Web服务都通过WSDL绑定声明了不同的WS-Policy断言。只有客户端SOAP请求中所携带的非功能性需求与当前服务的WS-Policy断言能力相符,该服务才可以被匹配从而进行调用。而一般情况下这种动态服务选取会将备选的Web服务元数据(包括WSDL,XSD以及WS-Policy)发布到一个元数据注册中心(目前IBM推荐的主流产品是WebSphere Registry & Repository),由请求中介在运行时进行查询匹配(WID V6.0.2已经有中介组件支持对WSRR的查询)。已有的基于WS-Policy框架的服务策略规范(如WS-RM, WS-Security等)都将每次服务调用中的非功能需求嵌在SOAP Header中,在这种情况下,服务中介必须实现基于SOAP Header的服务匹配;
- WebSphere Business Services Fabric(WBSF)是IBM新近推出的支持业务领域服务及服务策略建模、开发和运行时支持的新款产品。该产品已经在保险业得到广泛应用,并且成为IBM在SOA监管(Governance)方向的核心产品之一。基于该产品开发的业务策略也和每个备选的Web服务关联,并要求服务请求的SOAP Header中嵌入相应的与业务相关的选择条件,从而由WSBF做运行时策略使能,完成服务的选取。WSBF在未来也将架构于WPS/WESB之上,因而也面临本文所提及的问题。
本文的目的不是介入以上对相关IBM产品和Web服务规范应用的讨论,因而采用图1的简单的技术抽象来说明如何解决使用SCA Web服务绑定来传递SOAP Header这一问题。以下在第3节中将给出通常情况下使用WID来实现满足图1要求的SCA中介模块的基本做法,并说明问题所在;在第4节中将对第3节的配置进行修改,从而解决问题。
3. 初配SCA 中介模块——SOAP Header丢失
3.1 安装供选择的Web服务
从文章的“下载”一节中下载得到两个Echo Web服务的EAR文件EchoEJB1EAR.ear和EchoEJB2EAR.ear,并依照以下步骤安装并启动两个Echo Web服务:
- 启动WID后,切换到“J2EE” 视图,在Servers子窗口中选择“WebSphere ESB Server V6.0”并启动该服务器;
- 选择“File->Import->EAR file”,并导入以上两个Echo Web服务的EAR文件到当前的工作台中;
- 在Servers子窗口中右键点击“WebSphere ESB Server V6.0”,选择“Add and remove projects…”,并在弹出的窗口中选择添加“EchoEJB1EAR”和“EchoEJB2EAR”到WESB中。
至此,两个Echo Web服务已经成功完成了在WESB服务器中的安装和启动。
3.2 创建并配置SCA中介模块
接下来需要创建一个SCA中介模块来完成服务路由。依照以下步骤进行:
- 选择“File->New->Projects…->Mediation Module”,并在弹出的对话框中给定生成的中介模块名称为“EchoServiceRouter”。点击“Finish”;
- 拷贝以上导入的两个Web服务的WSDL文件到该中介模块工程中。具体的做法是:在“J2EE” 视图的“Project Explorer”子窗口中,选择“EJB Projects->EchoEJB1->ejbModule->META-INFO->wsdl->EchoSvr1.wsdl”,右键点击,选择Copy。切换到“Business Integration”视图,在“Business Integration”子窗口中选择“EchoServiceRouter->Interfaces”,右键点击并选择Paste。Echo Web服务1的WSDL被复制完毕。用同样的办法拷贝得到Echo Web服务2的WSDL;
- 在“EchoServiceRouter”工程下双击“Assembly Diagram”,打开该中介模块的集成图。重命名缺省生成的中介组件为“EchoServiceRouter”。右键点击该组件,选择“Add->Interface->EchoSvr1”。这样就把该中介模块对外的服务接口设为Echo Web服务1的接口类型。由于Echo Web服务2的接口类型与之不同,需要在编辑中介流程时对请求消息做消息格式转换;
- 在集成图空白处点击右键,选择“Add->Import”,重命名生成的导入组件为“EchoService1Import”。用与上一步相同的办法设置该导入组件的接口为EchoSvr1。右键点击该导入组件,选择“Generate Binding…->Web Service Binding”,在弹出的窗口中选择“Use an existing web service port”,并通过“Browse”按钮选择EchoSvr1 Web服务端口。设置完毕后,选中“EchoService1Import”组件,它的Properties子窗口如图2所示:
图2 “EchoService1Import”导入组件的绑定配置
- 用与上相同的方法添加导入组件“EchoService2Import”,设置它的接口类型和Web服务绑定为EchoSvr2。在集成图中选择Wire工具,分别将“EchoServiceRouter”组件连接到“EchoService1Import”和“EchoService2Import”;
- 右键点击“EchoServiceRouter”组件,选择“Generate Export…->Web Service Binding”,在弹出的窗口中选择“soap/http”,点击OK。重命名缺省生成的到处组件为“EchoServiceRouterExport”。选中“EchoServiceRouterExport”组件,它的Properties子窗口如图3所示:
图3 “EchoServiceRouterExport”导出组件的绑定配置
因为该中介模块要部署在服务端口号为9081的WESB上,所以将以上缺省生成的Web服务地址端口由9080改为9081,并保存。
- 在集成图空白处点击右键,选择“Arrange Contents Automatically”,并保存集成图。至此,“EchoServiceRouter”中介模块的组件集成如下图所示:
图4 “EchoServiceRouter”中介模块的集成图设置
3.3 在SCA中介模块中实现中介流
SCA中介模块中,中介组件的实现为中介流。为第2节所述的场景实现中介流的步骤如下所示:
- 在集成图中双击“EchoServiceRouter”组件,在弹出的对话框中选择Yes;接着在弹出的“Generate Implementation”对话框中选择OK,中介流编辑器被打开。对应于集成图中的导入、导出组件的三个操作在编辑器的上部被显示出来;
- 分别将左部EchoSvr1接口中的echo操作和右部EchoSvr1Partner接口中echo操作以及EchoSvr2Partner接口中的echoMessage操作连接起来;
- 现在可以在中介流编辑器的下部编辑中介流的具体行为了。在请求中介流的选择卡下,先拖拽一个“Message Filter”中介单元(Mediation Primitive)到编辑器中,它用于依据传入到中介模块的消息内容进行消息路由;再拖拽一个“XSL Transformation”中介单元到编辑器中,它用于转换从中介模块输入的消息格式到Echo Web服务2可接受的格式。最后拖拽两个“Message Logger”中介单元到编辑器中,它们分别用于将送往Echo Web服务1和Echo Web服务2的SMO以XML格式写入数据库,作为日志;
- 右键点击“Message Filter”中介单元,选择“Add Output Terminal”,接受弹出对话框的缺省设置;
- 如下图所示将以上的中介单元连接起来形成请求中介流:
图5 “EchoServiceRouter”中介模块的请求中介流
其中“XSL Transformation”中介单元有个红叉,主要是因为还没有对它的属性进行配置,在下面的步骤中我们将完成这个功能。注意,以上步骤生成的“Message Filter”中介单元新的输出口match1需要和“XSL Transformation”中介单元连接起来,而“Message Filter”原有的输出口default和“MessageLogger1”中介单元连接起来。
- 选中“Message Filter”中介单元,在Properties子窗口红选择detail项,在Filters一节点击Add按钮。在弹出的“Add/Edit”对话框中为该中介单元的match1输出口定义一个过滤条件,结果如下图所示:
图6 “Message Filter”中介单元在请求中介流中的过滤条件设置
该过滤条件“headers/SOAPHeader[1]/name[self::node()="routerCriterion"] and /headers/SOAPHeader[1]/nameSpace[self::node()="http://cn.ibm.com/example"] and /headers/SOAPHeader[1]/value/routerCriterion[self::node()="GOLD"]”表明当传入的消息满足的第一个SOAP Header项的名称为“routerCriterion”,值为“GOLD”时,消息由match1输出口流出;
- 选中“XSL Transformation”中介单元,在Properties子窗口红选择detail项,点击“New…”按钮。在弹出的“New XSLT Mapping”对话框中选择“Message Root”为“/”,并点击“Finish”以打开消息映射编辑器。如下图对请求消息格式进行映射:
图7 “XSL Transformation”中介单元在请求中介流中的消息映射
保存并关闭该映射文件;
- 分别选中两个“Message Logger”中介单元,在Properties子窗口红选择detail项,选择“Root”为“/”。在中介流编辑器中进行保存,这样就完成了整个请求中介流的编辑(现在上面的红叉将会消失);
- 在中介流编辑器下部的响应中介流选择页面上,用类似的方法如下编辑响应中介流:
图8 “EchoServiceRouter”中介模块的响应中介流
其中“XSL Transformation”中介单元的消息映射如下所示:
图9 “XSL Transformation”中介单元在响应中介流中的消息映射
这样我们就完成了整个中介流的设定。回到集成图,点击保存,该SCA中介模块被正确编译。
3.4 SCA中介模块的部署
在“Servers”子窗口右键点击“WebSphere ESB Server V6.0”,选择“Add and remove projects…”,并在弹出的窗口中选择添加“EchoServiceRouterApp”到WESB中。点击“Finish”后,就完成了在上一节开发的SCA中介模块在WESB服务器的部署。
3.5 安装并部署服务请求客户端
从文章的“下载”一节中下载得到服务请求客户端的WAR文件EchoSvrClientEAR.ear,并依照以下步骤安装到当前的工作台:
- 切换到“J2EE” 视图,选择“File->Import->EAR file”,并导入下载得到的服务请求客户端到当前的工作台中;
- 在Servers子窗口中右键点击“WebSphere ESB Server V6.0”,选择“Add and remove projects…”,并在弹出的窗口中选择添加“EchoSvrClientEAR”到WESB中。
至此,该服务请求客户端已经部署到了WESB服务器中。
在开始运行测试之前,再看一下以上服务请求客户端的实现的关键之处。除了一个JSP文件用于生成客户端测试界面,和发起对SCA中介模块导出的Web服务的请求调用外,该JSP引用了一个Java Bean,用于构造客户端的SOAP消息,并发到SCA中介模块所导出的Web服务地址上。以下给出了关键代码片断,完整的代码实现在动态网页工程EchoSvrClient中的com.ibm.example.bean.EchoBean.java中。
列表1 客户端的关键代码片段
public String echo(String msg) throws Exception {
...
SOAPMessage message = mf.createMessage();
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope env = soapPart.getEnvelope();
//构造SOAP Body
SOAPBody body = env.getBody();
Name name = env.createName("echo", "q0", "http://ejbs");
SOAPBodyElement bodyElem = body.addBodyElement(name);
name = env.createName("message");
SOAPElement soapElem = bodyElem.addChildElement(name);
soapElem.addTextNode(msg);
//构造SOAP Header
SOAPHeader header = env.getHeader();
name = env.createName("routerCriterion", "exp",
"http://cn.ibm.com/example");
SOAPHeaderElement headerElem = header.addHeaderElement(name);
headerElem.addTextNode("GOLD");
message.saveChanges();
...
}
|
从以上片断可以看出,该除了在SOAP消息中添加基本的消息体用于表达请求的操作和参数外,在SOAP Header中添加了一个名为“routerCriterion”,值为“GOLD”的Header项,这与SCA中介模块请求中介流中的消息过滤条件一致。
3.6 运行测试
依照以下步骤运行该实例:
- 打开浏览器,输入http://localhost:9081/EchoSvrClient/request.jsp,并回车;
- 在显示的网页中的文本框中输入要被Echo的消息内容,例如“This is just a test!”,点击“Invoke”按钮;
- Web服务成功返回,网上上显示被Echo的消息为“[Echo Service2] This is just a test!”。
问题出来了,这与原先设想的由Echo Web服务1来Echo这条消息出现了误差。根据在SCA中介模块中的配置,从客户端发出的SOAP消息应该具有相应的SOAP Header,并且满足请求中介流中消息过滤中介组件的match1输出口的条件,这样该消息应该被路由到对应于Echo Web服务1的导入组件。为什么会这样?
好在通过以上的中介流设置已经对所有的中介路径都做了消息日志,因此可以到WESB对应的消息日志数据库中去查看一下刚刚在中介组件中传递的SMO消息内容,结果是:
列表2 错误配置下中介组件在日志中所捕获的SMO
<?xml version="1.0" encoding="UTF-8"?>
<ServiceMessageObject:smo
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ServiceMessageObject=
"http://www.ibm.com/websphere/sibx/smo/v6.0.1"
xmlns:ejbs="http://ejbs">
<context/>
<headers>
<SMOHeader>
<MessageUUID>A76B6DA1-0111-4000-E001-08F409BA717F</MessageUUID>
<Version>
<Version>6</Version>
<Release>0</Release>
<Modification>2</Modification>
</Version>
<MessageType>Request</MessageType>
</SMOHeader>
</headers>
<body xsi:type="ejbs:echoMessageRequest">
<echoMessage>
<message>This is just a test!</message>
</echoMessage>
</body>
</ServiceMessageObject:smo>
|
很明显,消息路由的错误是因为SOAP Header根本没有进入SCA中介模块,并得到SMO的传输。难道这是WESB的一个bug吗?其实不是,原因在于先前在SCA中介模块中的配置有问题。
4. 重配SCA中介模块——SOAP Header被传递
原来,WESB的运行时环境在经过Web服务绑定时需要将由SOAP格式表达的消息转换为DataObject类型的Java对象。在这个转换的过程中,WESB需要预先装载SOAP消息中各个XML元素的XML Schema类型定义,不妨称之为SOAP消息的内容数据。装载完毕后,每当一个SOAP实例到来,在转换的过程中,首先从已经装载的元数据列表中查找对应的元数据项,并根据这个元数据项,使用XSD到DataObject的映射规则,生成相应的DataObject对象容器,而后才会将SOAP消息中的具体数据填入DataObject。如果一旦没有找到对应的XSD元数据,该项转换就会被忽略。
那么为什么从上面的SMO内容看SOAP Body部分被转换了而SOAP Header则没有呢?很明显,我们在SCA中介模块的配置过程中已经指定了所有涉及的WSDL文件,这些文件描述了SOAP Body中可能会用到的XML元素的XSD(直接在WSDL文件中的Schema标签下定义,或者通过导入XSD文件给出),它们其实也就是服务调用的请求、响应数据格式。但是我们并没有给出SOAP Header部分的XSD定义,当然WESB运行时环境也就不认识它了。
知道了原因,解决的办法也很简单:
- 在WID的工作台中切换到“Business Integration”视图。在“Business Integration”子窗口中选择“EchoServiceRouter->Data Types”。点击右键,选择”New->Business Object”;
- 在弹出的“New Business Object”对话框中去掉“Namespace”右端的“Default”复选框选择,并输入“http://cn.ibm.com/example”;在“Name”栏输入“routerCriterion”,点击Finish。关闭打开业务数据编辑器,在“Data Types”下选中刚刚创建的“routerCriterion”,点击右键,选择“Open With->Text Editor”。如下修改“routerCriterion”的XSD定义:
列表3 为要传递的SOAP Header制定的XSD
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://cn.ibm.com/example">
<xsd:element name="routerCriterion" type="xsd:string"/>
</xsd:schema>
|
- 保存后,在“Business Integration”子窗口中选择“EchoServiceRouter->Web Service Ports->EchoSvr1”,点击右键,选择“Open With->Text Editor”。在打开的编辑器中,对该WSDL文件的Schema元素做如下修改:
列表4 在中介模块的WSDL文档中添加的对SOAP Header XSD的引用
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ejbs"
xmlns:impl="http://ejbs" xmlns:intf="http://ejbs"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
|--10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace="http://ejbs"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://cn.ibm.com/example" schemaLocation="routerCriterion.xsd"/>
|--10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
...
</schema>
</wsdl:types>
...
</wsdl:definitions>
|
- 保存并在WESB上重新部署SCA中介模块工程。
如第3节中的那样重新运行客户端调用,这时候网页上显示的调用结果为“[Echo Service1] This is just a test!”。 到WESB对应的消息日志数据库中去查看一下刚刚在中介组件中传递的SMO消息内容,结果是:
列表5 正确配置下中介组件在日志中所捕获的SMO
<?xml version="1.0" encoding="UTF-8"?>
<ServiceMessageObject:smo
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ServiceMessageObject=
"http://www.ibm.com/websphere/sibx/smo/v6.0.1"
xmlns:ejbs="http://ejbs"
xmlns:example="http://cn.ibm.com/example">
<context/>
<headers>
<SMOHeader>
<MessageUUID>B05B01BF-0111-4000-E000-0E6809BA717F</MessageUUID>
<Version>
<Version>6</Version>
<Release>0</Release>
<Modification>2</Modification>
</Version>
<MessageType>Request</MessageType>
</SMOHeader>
<SOAPHeader>
<nameSpace>http://cn.ibm.com/example</nameSpace>
<name>routerCriterion</name>
<value xsi:type="example:">
<example:routerCriterion>GOLD</example:routerCriterion>
</value>
</SOAPHeader>
</headers>
<body xsi:type="ejbs:echoRequest">
<echo>
<message>This is just a test!</message>
</echo>
</body>
</ServiceMessageObject:smo>
|
可以看到,这一次由于指定了SOAP Header项的XSD,WESB运行时环境能够生成相应的DataObject对象容器,SOAP Header通过Web服务绑定得到了传输,在中介模块的请求中介流中,服务过滤中介单元也能正确的完成消息的路由。
5. 结束语
本文通过对一个具体的基于SCA中介模块根据SOAP Header做服务路由的场景的技术分析,说明了在WID中开发类似应用时所需要特别注意的配置,从而也阐明了WPS或者WESB运行时环境在Web服务绑定上完成SOAP消息到DataObject对象转换的基本原理。
本文对SOAP Header通过SCA Web服务绑定进行传递这一技术细节的探讨在于旨在抛砖引玉,希望所有在使用WID、WPS和WESB做实际项目开发和原型设计的软件工程师们能从中获益,提出在SCA编程模式下更新、更有实用价值的模式。如果本文有幸帮助一些技术人员解决了他们的技术难题,那是我们最大的收获。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| Echo Web服务1的EAR文件 | EchoEJB1EAR.ear | 60KB | HTTP |
|---|
| Echo Web服务2的EAR文件 | EchoEJB2EAR.ear | 60KB | HTTP |
|---|
| SCA中介模块项目交换文件 | EchoServiceRouter.zip | 30KB | HTTP |
|---|
| 服务请求客户端EAR文件 | EchoSvrClientEAR.ear | 10KB | HTTP |
|---|
参考资料
作者简介  | 
|  | 刘昕鹏,IBM软件工程师,工作在IBM中国软件开发实验室 - SOA Design Center,从事SOA Policy及SOA Governance相关的工作,对J2EE、SOA、MDA/MDD、AOP、语义网等相关技术有浓厚兴趣。作者目前主要负责SOA Policy Toolkits项目的设计与开发工作。 |
 | 
|  | 薛亮,IBM资深软件工程师,工作在IBM中国软件开发实验室 - SOA Design Center,从事SOA Policy及SOA Governance相关的工作,对J2EE、SOA、Registry and Repository等相关技术有浓厚兴趣。作者目前主要从事Registry and Repository Federation项目的设计与开发工作。 |
 | 
|  | 高希斌,现为中科院研究生院硕士研究生 ,2006.7~2006.12期间在IBM中国软件开发实验室 SOA Design Center 实习,期间从事SOA Policy相关的工作。作者目前主要从事网格方面的开发。 |
对本文的评价
|