实现 SOAP 和 JMS 消息头的转换

日期: 2007-12-10 作者:李传峰 来源:TechTarget中国

  作为一种开放架构,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

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • 事件驱动框架和SOA在空军的应用

    空军正在利用SOA来改善数据共享,并实时跟踪战机,美国空军机动司令部的Michael Marek解释了企业可从中学习的经验。

  • 揭秘New Relic APM技术细节

    New Relic应性能管理(APM)套件主要用于Web软件开发。它允许用户在面向服务的架构(SOA)上跟踪关键事务性能,并且支持代码级别的可见性来评估特定代码段和SQL语句对性能的影响

  • 仅凭SOA和云无法解决业务数据管理风险问题

    SOA和云可以是某些恼人问题高效的解决方案;这一点我们已经知道了。但是也要记住它们并不是所有事情的直接答案,特别是当你的问题是业务数据管理风险,而不是技术问题时。

  • 总线技术究竟该不该用?

    曾几何时企业服务总线(ESB)被视为企业IT的核心。今天,不仅ESB受到了比被废弃还要糟糕的攻击,若干开发趋势似乎对更简单的消息总线也发起了质疑。