IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services | WebSphere  >

基于 WESB/WPS 实现 SOAP 和 JMS 消息头的转换

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 中级

李 传峰 (lichuanf@cn.ibm.com), IBM中国软件研发实验室SOA设计中心工程师

2007 年 4 月 30 日

本文重点阐述如何基于 WESB 实现 SOAP 和 JMS 消息头的转换,以及如何在 WESB 中处理 SOAP 和 JMS 消息头。

概述

作为一种开放架构,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 系统架构图
图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 创建中介模块
图2 创建中介模块

1)创建一个中介模块,其中包含两个Business Object类型:BodyType和HeaderType。HeaderType中有一个名为“priority”的整型元素,标识消息的优先级。

2)创建一个接口,输入、输出参数类型都是BodyType,接口名称为HeaderMedInterface。如上图所示。

2. 创建中介流程

1)创建一个中介流程,源接口和目标接口都是HeaderMedInterface,流程名称为HeaderMedFlow。


图3 创建中介流程
图3 创建中介流程

2)如上图所示,在Request Flow中创建客户化中介原语CusMediation。


图4 定义客户化中介原语
图4 定义客户化中介原语

3)定义客户化中介原语。注意不能用缺省的“/body”作为Message Root,因为“/body”只能处理消息体,不能处理消息头。这里选取“/”作为Message Root,可以处理整个消息,如上图所示。定义原语的过程中,需要创建一个新的接口,接口名称为CusMedInterface,并自动生成Java实现sca.component.mediation.java.impl.CusMedInterfacePartner。

3. 组装集成应用


图5 组装集成应用
图5 组装集成应用

1)创建一个中介组件,其实现为上边创建的中介流程HeaderMedFlow。该中介组件有两个引用,HeaderMedInterfacePartner和CusMedInterfacePartner。

2)为中介组件创建一个Export,选取soap/http的Web service绑定。生成的WSDL文件的缺省名称为HeaderMedFlowExport_HeaderMedInterfaceHttp_Service.wsdl。


图6 创建Import
图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 测试客户端
图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消息头。消息头常被用来实现非功能性需求,这种场合下,本文具有更加直接的参考意义。






回页首


下载

描述名字大小下载方法
Web测试客户端HeaderMedClientEAR.ear37KBHTTP
消息中介样例HeaderMedModule.zip34KBHTTP
测试消息驱动BeanHeaderMedServiceEAR.ear5KBHTTP
关于下载方法的信息


参考资料



关于作者

李传峰:IBM中国软件研发实验室SOA设计中心工程师,多年IT企业工作经验,对软件开发及应用有浓厚兴趣。目前正在从事ESB和SCA的实现工作。联系方式:lichuanf@cn.ibm.com




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款