作为一种开放架构,SOA(Service Oriented Architecture)可以支持多种协议和消息格式。通过ESB(Enterprise Service Bus)集成不同协议和消息格式的Service,将各种消息格式转换成通用对象模型(Common Object Model),屏蔽消息间的差异。
IBM的WESB(WebSphere ESB)产品支持多种绑定方式,如Web service绑定、JMS绑定等。通过这些绑定,WESB将外部消息统一转换为SDO(Service Data Object)。IBM developerWork上有不少关于WESB上Web service和JMS绑定的文章,对怎样处理SOAP和JMS消息体有很充分的阐述,这里不再赘述。
本文重点阐述如何基于WESB实现SOAP和JMS消息头的转换,以及如何在WESB中处理SOAP和JMS消息头。
由于WPS(WebSphere Process Server)与WESB完全兼容,本文基于WPS实现,版本为6.0.1.3。开发工具为WID(WebSphere Integration Developer) V6.0.1.2。
关于消息头
对于网络上传输的消息,其承载的内容常常分为消息头和消息体,如相对底层的IP、TCP、UDP消息,以及处于应用层的HTTP消息。业务相关内容存入消息体中,消息头中包含与业务无关的管理信息,比如消息的优先级、序列号、地址信息等。
SOA中的SOAP和JMS消息同样也包含消息头和消息体。
SOAP消息头
SOAP消息头的一个典型应用是传送与安全相关的信息,如消息的数字签名,身份认证的令牌(Token)等,具体可以参考WS Security系列规范。另外还有很多Web service规范,如WS Addressing、WS Policy,也需要扩展SOAP消息头实现。
SOAP消息头不仅可以传送Web service规范中定义的元素,也可以传送用户自定义的元素。SOAP消息头中的元素遵循XML格式。
JMS消息头
JMS消息头中可以包含一些预先定义的标准属性,如JMSDestination、JMSMessageID、JMSPriority等。JMS消息头中也可以包含用户自定义的属性,自定义属性采用“属性名,属性类型,属性值”的三元组格式。
相对于SOAP消息头的灵活的XML格式,JMS消息头的格式更加严格。
消息头转换的应用场景
为了提高效率,某企业需要将一个内部的XML over JMS Service包装成Web Service,使合作伙伴和客户也可以从外部调用。JMS Service可以区分服务请求的优先级,该企业要求包装后的Web Service也支持区分服务水平的能力。
JMS Service通过JMS消息头中的JMSPriority属性区分服务请求的优先级。相应地,在Web service的SOAP消息头中定义标识优先级的元素。
图1 系统架构图
如上图所示,在WESB中,通过Web service绑定和JMS绑定,转换SOAP和JMS协议和消息格式,同时通过消息中介转换SOAP和JMS消息头中的优先级相关属性。
后边将逐步构建这个场景。JMS Service由一个消息驱动Bean(Message Driven Bean)实现,将收到的JMS消息体原样返回。在WESB的客户化消息中介中实现SOAP消息头到JMS消息头的转换。外部用户通过Web客户端访问暴露出来的Web service。
下载区中包含了Web客户端、中介模块和消息驱动Bean的项目代码。
创建客户化消息中介的框架
WESB不仅支持预定义的消息中介模式,如XSL Transformation、Message Filter、Message Logger、Database Lookup等,还支持用户自定义的消息中介(Custom Mediation)。用户可以在客户化消息中介中编写Java代码,灵活处理输入消息对象,构造用户需要的输出消息对象。
本文将在客户化消息中介中实现SOAP和JMS 消息头的转换。以下简述创建客户化消息中介的关键步骤。可以参考下载区中已经完成的客户化消息中介HeaderMedModule.zip。
1. 创建中介模块
图2 创建中介模块
1)创建一个中介模块,其中包含两个Business Object类型:BodyType和HeaderType。HeaderType中有一个名为“priority”的整型元素,标识消息的优先级。
2)创建一个接口,输入、输出参数类型都是BodyType,接口名称为HeaderMedInterface。如上图所示。
2. 创建中介流程
1)创建一个中介流程,源接口和目标接口都是HeaderMedInterface,流程名称为HeaderMedFlow。
图3 创建中介流程
2)如上图所示,在Request Flow中创建客户化中介原语CusMediation。
图4 定义客户化中介原语
3)定义客户化中介原语。注意不能用缺省的“/body”作为Message Root,因为“/body”只能处理消息体,不能处理消息头。这里选取“/”作为Message Root,可以处理整个消息,如上图所示。定义原语的过程中,需要创建一个新的接口,接口名称为CusMedInterface,并自动生成Java实现sca.component.mediation.java.impl.CusMedInterfacePartner。
3. 组装集成应用
图5 组装集成应用
1)创建一个中介组件,其实现为上边创建的中介流程HeaderMedFlow。该中介组件有两个引用,HeaderMedInterfacePartner和CusMedInterfacePartner。
2)为中介组件创建一个Export,选取soap/http的Web service绑定。生成的WSDL文件的缺省名称为HeaderMedFlowExport_HeaderMedInterfaceHttp_Service.wsdl。
图6 创建Import
3)创建一个Import,连接到引用HeaderMedInterfacePartner。Import选取JMS绑定类型。如上图所示,输入JMS资源的JNDI名称,序列化类型选取“Business Object XML using JMS TextMessage”,不启用“JMS Function Selector”。
4)创建一个Java组件,连接到引用CusMedInterfacePartner,其Java实现为定义客户化中介原语过程中生成的sca.component.mediation.java.impl.CusMedInterfacePartner。
编写客户化消息中介的代码
对于定义客户化中介原语过程中自动生成的Java实现类sca.component.mediation.java.impl.CusMedInterfacePartner,需要在其mediate()方法中需要填入客户化消息中介的处理逻辑。
/**
* Method generated to support implemention of operation "mediate" defined for WSDL port
* type named "interface.CusMedInterface".
*
* The presence of commonj.sdo.DataObject as the return type and/or as a parameter
* type conveys that its a complex type. Please refer to the WSDL Definition for more
* information on the type of input, output and fault(s).
*/
public DataObject mediate(DataObject input1) {
// To override the generated Java Snippet, please comment out the following method call
sca.component.mediation.java.impl.CusMedInterfacePartnerCustomLogic.JavaSnippet snippet
= new sca.component.mediation.java.impl.CusMedInterfacePartnerCustomLogic
.JavaSnippet();
return snippet.execute(input1);
}
自动生成的mediate()方法进一步调用sca.component.mediation.java.impl.CusMedInterfacePartnerCustomLogic的execute ()方法。在execute ()方法中编写以下代码。
/**
* The following method is required for Java snippet support. Do not remove.
* @generated (com.ibm.wbit.java)
*/
public DataObject execute(DataObject input1) {
// covert input1 to ServiceMessageObject type
ServiceMessageObject smo = (ServiceMessageObject) input1;
// get headers in the SMO
HeadersType headers = smo.getHeaders();
// get SOAP header element
if (headers.getSOAPHeader().size() > 0) {
SOAPHeaderType soapHeader = (SOAPHeaderType) headers.getSOAPHeader().get(0);
DataObject soapHeaderDO = (DataObject) soapHeader.getValue();
System.out.println(OutputHelper.print(soapHeaderDO));
// get priority
int priority = soapHeaderDO.getInt("priority");
System.out.println("Priority in SOAP Header: " + priority);
// create a new JMSHeaderType
JMSHeaderType jmsHeader = ServiceMessageObjectFactory.eINSTANCE.
createJMSHeaderType();
jmsHeader.setJMSPriority(BigInteger.valueOf(priority));
// set JMS header element
headers.setJMSHeader(jmsHeader);
// delete SOAP header
smo.getHeaders().getSOAPHeader().remove(0);
}
return (DataObject) smo;
}
还记得在创建客户化消息中介的框架时,选取消息类型的Message Root为“/”,这决定了客户化消息中介的具体输入类型为ServiceMessageObject(SMO)。
SMO中的SOAP消息头是一个SDO的列表,其中每个SDO的结构由XML Schema定义。本文示例中的SDO列表中只有一个SDO,类型为上边创建的HeaderType。
SMO中的JMS消息头为JMSHeaderType类型的SDO,支持采用直接的方法访问SDO中的元素,这里调用了setJMSPriority()方法。
到现在为止,客户化消息中介已经构建完成,下边将构建用于测试的客户端和JMS Service。
构建测试客户端和JMS Service
1. 构建JMS Service
请参考下载区中已经完成的消息驱动Bean HeaderMedService.ear。
JMS Service实现为一个消息驱动Bean,其onMessage()方法的代码如下。
public void onMessage(javax.jms.Message msg) {
try {
System.out.println("Enter MDB …");
// print the value of JMSPriority
System.out.println("JMSPriority is: " + msg.getJMSPriority());
// get payload of the JMS message
String payload = ((TextMessage) msg).getText();
// get message id of the JMS message
String MessageID = msg.getJMSMessageID();
// get JMS resources to return the response message
InitialContext context = new InitialContext();
Queue q = (Queue) context.lookup("java:comp/env/test/sender");
QueueConnectionFactory qcf = (QueueConnectionFactory) context.
lookup("java:comp/env/test/QCF");
QueueConnection conn = qcf.createQueueConnection();
conn.start();
QueueSession session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender sender = session.createSender(q);
// create the response message with the same payload as the request message
TextMessage sendMsg = session.createTextMessage(payload);
// set JMSCorrelationID of the response message to correlate with the request message
sendMsg.setJMSCorrelationID(MessageID);
// send the response message
sender.send(sendMsg);
// release the JMS resources
sender.close();
session.close();
conn.close();
System.out.println("Exit MDB …");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
该消息驱动Bean首先打印出JMSPriority的值,然后保持消息体不变,构建并返回响应的JMS消息。构建响应消息时,需要将其JMSCorrelationID设置为申请消息的JMSMessageID,以配对申请消息和响应消息。
2. 构建客户端
请参考下载区中已经完成的Web客户端HeaderMedClientEAR.ear。
外部用户通过Web访问暴露的Web service。这里使用JAX-RPC Handler生成SOAP消息头,代码如下。
public boolean handleRequest(MessageContext context) {
// Fill in method body or delete method to use GenericHandler
System.out.println("handler …");
SOAPMessageContext soapContext = (SOAPMessageContext) context;
try {
SOAPHeader header = soapContext.getMessage().getSOAPPart()
.getEnvelope().getHeader();
// add HeaderType element
SOAPElement header1 = header.addChildElement("HeaderType", "header",
"http://HeaderMedModule");
// add priority element
SOAPElement header2 = header1.addChildElement("priority");
// add value of the priority
header2.addTextNode("2");
} catch (SOAPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
JAX-RPC Handler为SOAP申请消息添加SOAP消息头,消息头的格式符合上边创建的HeaderType的类型定义。
测试应用场景
图7 测试客户端
Web service客户端发送的SOAP申请消息如下。
<soapenv:Envelope xmlns_soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns_soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns_xsd="http://www.w3.org/2001/XMLSchema"
xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<header:HeaderType xmlns_header="http://HeaderMedModule">
<priority>2</priority>
</header:HeaderType>
</soapenv:Header>
<soapenv:Body>
<p448:operation1 xmlns_p448="http://HeaderMedModule/HeaderMedInterface">
<InputPara>
<payload>aaa</payload>
</InputPara>
</p448:operation1>
</soapenv:Body>
</soapenv:Envelope>
可以看到,SOAP申请消息中包含了类型为HeaderType的消息头,标识消息优先级为2。
SOAP消息经过中介模块后,转换为JMS消息到达MDB,系统输出如下。
[07-1-22 17:39:24:906 CST] 00000069 SystemOut O (DataObject: HeaderType) {
priority=(Data)’2′
}
[07-1-22 17:39:24:906 CST] 00000069 SystemOut O Priority in SOAP Header: 2
[07-1-22 17:39:25:000 CST] 00000096 SystemOut O Enter MDB …
[07-1-22 17:39:25:000 CST] 00000096 SystemOut O JMSPriority is: 2
首先,SOAP消息头经过Web service绑定的Export转换为SDO,然后,在客户化消息中介中取出priority并构建JMS消息头对应的SDO,并通过JMS绑定的Import转换为JMS消息头。
总结
本文通过一步步的构建一个模拟的应用场景,展示怎样基于WESB/WPS处理和转换SOAP/JMS消息头。消息头常被用来实现非功能性需求,这种场合下,本文具有更加直接的参考意义。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
作者
相关推荐
-
事件驱动框架和SOA在空军的应用
空军正在利用SOA来改善数据共享,并实时跟踪战机,美国空军机动司令部的Michael Marek解释了企业可从中学习的经验。
-
揭秘New Relic APM技术细节
New Relic应性能管理(APM)套件主要用于Web软件开发。它允许用户在面向服务的架构(SOA)上跟踪关键事务性能,并且支持代码级别的可见性来评估特定代码段和SQL语句对性能的影响
-
仅凭SOA和云无法解决业务数据管理风险问题
SOA和云可以是某些恼人问题高效的解决方案;这一点我们已经知道了。但是也要记住它们并不是所有事情的直接答案,特别是当你的问题是业务数据管理风险,而不是技术问题时。
-
总线技术究竟该不该用?
曾几何时企业服务总线(ESB)被视为企业IT的核心。今天,不仅ESB受到了比被废弃还要糟糕的攻击,若干开发趋势似乎对更简单的消息总线也发起了质疑。