内容


IBM WebSphere 开发者技术期刊

使用 JMS 和 WebSphere ESB 构建强大而可靠的 SOA——第 3 部分

WebSphere ESB 中介

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: IBM WebSphere 开发者技术期刊

敬请期待该系列的后续内容。

此内容是该系列的一部分:IBM WebSphere 开发者技术期刊

敬请期待该系列的后续内容。

摘自 IBM WebSphere 开发者技术期刊

引言

第 1 部分中,我们介绍了企业服务总线 (ESB) 的概念,在第 2 部分中,我们演示了如何建立测试应用程序客户端和提供程序。本系列中的三篇文章介绍了如何通过 JMS 和 IBM WebSphere ESB 建立服务提供者和使用者之间的连接,在最后这篇文章中,我们将研究如何使用带有自定义 JMS 绑定的中介流组件来最终建立连接。

如果您还记得第 1 部分,我们曾讨论过构成 WebSphere ESB 基础的编程模型,即服务组件体系结构 (SCA)。在这个体系结构中,不同类型的组件都被看作服务,并且可使用相同的方式进行访问。WebSphere ESB 中介流是这些组件类型中的一种。与任何 SCA 组件一样,可以通过中介流所提供的导出对其进行访问,并且中介流可以通过导入将消息转发到其他的外部服务。JMS 的特殊类型的导入和导出,称为JMS 绑定,允许用户指定绑定配置并编写自己的数据处理代码。中介流本身包含一系列的中介基元,在消息通过总线时,可以使用这些中介基元对消息进行相应的操作。

概括地说,最后的这篇文章将:

  • 介绍如何为 JMS 绑定编写处理各种 JMS 消息类型的数据处理插件。
  • 说明如何建立充分利用自定义绑定代码的导出和导入。
  • 描述使用 WebSphere Integration Developer 的一个简单中介流的实现。
  • 将所有的内容组合到一个中介模块中,您可以将这个中介模块部署到 WebSphere ESB 运行时,并且这将允许您运行整个应用程序。

下载部分中包含了完整的项目交换 ZIP 文件,您可以将它导入到 WebSphere Integration Developer,并且其中包含了所有的代码和运行该示例所需的其他内容。为了节省时间,并使得我们可以把主要精力放在接下来的主题,请在继续学习本文前下载并导入这个文件。本文余下的内容假设您已完成了这项操作。在描述该解决方案中的各个部分时,我们将说明在导入了该文件后,每个部分位于 WebSphere Integration Developer 中的何处。

JMS 自定义绑定

在为导入或导出创建 JMS 绑定时,您需要确定用来处理传入和传出消息的两个 Java 类。一个类称为函数选择器,另一个类称为 数据绑定。我们还需要定义用于入站和出站 JMS 流量的 JMS 队列的名称。让我们更仔细地看看这些部分。

函数选择器

与所有的 SCA 服务组件一样,WebSphere ESB 中介具有一个接口。在 WebSphere ESB 中,将该接口表示为 WSDL PortType,这意味着每个中介流组件支持一项或多项操作(或方法,在此上下文中,这两个术语表示相同的含义)。然而,JMS 消息仅包含数据,而不包含它想要进行的任何目标操作的指示。所以,我们需要将特定的 JMS 消息映射到目标服务接口的特定操作。这正是函数选择器所完成的工作。

在我们的示例中,我们已经为每个 JMS 消息类型(可在 JMSCustomBindingLibrary/JMSCustomBindingInterface.wsdl 中找到)定义了具有一项操作的服务接口。例如,应该将 JMS 文本消息发送到 handleText() 操作,而将流消息发送到 handleStream() 操作等等。稍后您将看到,我们可以为不同的操作指定不同的数据绑定。

清单 1 显示了我们的示例中函数选择器的代码段:

清单 1. 函数选择器代码段
public class SimpleJMSFunctionSelector implements FunctionSelector {
	public String generateEISFunctionName(Object[] arguments) {

	... ...
	Message message = (javax.jms.Message) arguments[0];
	functionName = message.getJMSType();

	if (functionName == null) {
		if (message instanceof BytesMessage)
			messageBodyType = "Bytes";
		else {
			if (message instanceof TextMessage)}
				messageBodyType = "Text";
	... ...
	functionName = "handle" + messageBodyType;
	... ...
}

请注意 generateEISFunctionName() 方法如何返回关联于指定消息的函数(或操作,或方法)的名称。在我们的示例中,函数名称为“handle”加上该消息类型。例如,对于 JMS 文本消息,将返回“handleText”。换句话说,当中介接收到一个 JMS 文本消息时,会将它发送到 handleText() 方法。

(您可以在 JMSCustomBindingClasses 项目的 SimpleJMSFunctionSelector.java 文件中找到完整的源代码。)

在导出的绑定属性中,在 Connection 选项卡的高级设置下,对函数选择器进行了定义,如图 1 所示。

图 1. 在 WebSphere Integration Developer 中对函数选择器进行设置
图 1. 在 WebSphere Integration Developer 中对函数选择器进行设置
图 1. 在 WebSphere Integration Developer 中对函数选择器进行设置

WebSphere Integration Developer 还提供了一个缺省的函数选择器,称为 com.ibm.websphere.sca.jms.selector.impl.JMSFunctionSelectorImpl,它从传入的 JMS 消息的 Header 属性 TargetFunctionName 处获取指定的字符串值作为函数的名称。

数据绑定

数据绑定的实现是 JMS 自定义绑定的关键部分。在导出绑定的情况下,这个数据绑定实现必须清楚每个传入消息的格式和结构,并将其转换为适合于该中介流的格式,即 SDO DataObject。在导入绑定的情况下,则进行相反的处理,也就是说,将传出的 SDO DataObject 转换为发送到外部服务的 JMS 消息。

SCA 要求每个服务组件都具有一个接口,该接口可以是一个 Java 接口,或者描述为 WSDL portType(WebSphere ESB MediationFlow 组件支持 WSDL 接口)。要接收任意的 JMS 消息作为中介流中的成员,我们必须定义表示这些消息的一组操作。例如,JMS 文本消息由下面的 WSDL portType 操作来表示:

清单 2. JMS 文本消息的 portType 操作
p<wsdl:portType name="JMSCustomBindingInterface">
  <wsdl:operation name="handleText">
      <wsdl:input message="wsdl1:JMSTextMessage"/>
      <wsdl:output message="wsdl1:JMSTextMessage"/>
      <wsdl:fault message="wsdl1:JMSTextMessage" name="TextFault"/>
  </wsdl:operation>
... ...
</wsdl:portType>

(可以在 JMSCustomBindingLibrary 项目的 JMSCustomBindingInterface.wsdl 文件中找到完整的操作列表。)

在为导出或导入绑定定义了该接口之后,您需要提供处理 DataObject 和 JMS 之间转换的数据绑定类。它可以是针对整个导出/导入的一个类,或者每个操作一个类。我们已经在接口中定义了几项操作(每个 JMS 消息类型一项操作),因此我们定义了方法级的数据绑定,如图 2 所示。

图 2. 方法级数据绑定定义
图 2. 方法级数据绑定定义
图 2. 方法级数据绑定定义

每个数据绑定类都必须实现 com.ibm.websphere.sca.jms.data.JMSDataBinding 接口。

在我们的测试应用程序中,共有 5 个 JMSDataBinding 实现:

  • JMSTextDataBinding
  • JMSBytesDataBinding
  • JMSStreamDataBinding
  • JMSMapDataBinding
  • JMSObjectDataBinding.

您可以在 JMSCustomBindingClasses 项目中找到它们的源文件和其他的实用工具类。

一个数据绑定实现必须提供对下列方法的实现:

  • setDataObject() 设置表示 JMS 消息有效负载的 DataObject 的值。DataObject 必须符合相应的模式定义。

  • getDataObject() 返回表示 JMS 消息有效负载的 DataObject,并且该对象符合相应的模式。

  • getMessageType() 返回 JMS 消息的类型(在 JMSDataBinding 接口中定义)。

  • read(Message message) 根据 JMS 消息设置 DataObject 属性的值,JMS 消息作为方法参数提供。

  • write(Message message) 根据 DataObject 的属性设置 JMS 消息有效负载的值,JMS 消息作为方法参数提供。

  • isBusinessException() 表示是否将该 JMSDataBinding 实例所表示的 DataObject 作为一项业务异常。

  • setBusinessException() 设置是否将该 JMSDataBinding 实例所表示的 DataObject 作为一项业务异常。

图 3. 带 JMS 绑定的 SCA 导入/导出的方法执行流
图 3. 带 JMS 绑定的 SCA 导入/导出的方法执行流
图 3. 带 JMS 绑定的 SCA 导入/导出的方法执行流

在图 3 中,当 JMS 消息发送到该中介模块(即导出)时发生 JMS-->SDO 流,而在该中介模块(即导入)发送 JMS 消息时发生 SDO-->JMS 流。请注意,带 JMS 绑定的导入的响应消息并没有经过函数选择器。

清单 3 显示了 com.ibm.websphere.sibx.samp.jms.JMSTextDataBinding 类的部分源代码,该类对所有传入和传出的 JMS 文本消息进行处理。

清单 3. JMSTextBinding 代码示例
public class JMSTextDataBinding extends AbstractJMSDataBindingImpl 
                                implements JMSDataBinding {
private String payload = null;
private DataObject jmsData = null;

public int getMessageType() {
	return JMSDataBinding.TEXT_MESSAGE;
}
public void read(Message message) throws JMSException {
... ...
TextMessage textMessage = (TextMessage) message;
	payload = textMessage.getText();
... ...
// construct a DataObject based on the schema definition for
	// "JMSTextMessage"
	jmsData = DataFactory.INSTANCE.create("com.ibm.ws.sib.mfp/schema","JMSTextBody");
... ...
	// set the "value" property to the String value of the message payload
	jmsData.setString("value", payload);
... ...
}


public void write(Message message) throws JMSException { ... ... TextMessage textMessage = (TextMessage) message; // Clears the body of the JMS message textMessage.clearBody(); // Sets the value of the JMS message payload from the DataObject's // 'value' property payload = jmsData.getString("value"); textMessage.setText(payload); ... ... } public DataObject getDataObject() throws DataBindingException { return jmsData; } public void setDataObject(DataObject jmsData) throws DataBindingException { this.jmsData = jmsData; } }

read() 方法将 JMS TextMessage 转换为数据对象,而 write() 方法将数据对象重新转换为 JMS TextMessage。在 read() 方法中,使用相应的模式创建了一个新的数据对象,在部署该中介模块的过程中,WebSphere ESB 运行时可以使用该数据对象。在 write() 方法中,代码读取了数据对象的内容,并将其存储在传递的“message”参数中。通过 getMessagetype() 方法来确定消息的类型。

使用下面的一行代码来读取 DataObject 的有效负载:

payload = jmsData.getString("value");

这段代码使用了标准的 SDO API 调用。检索属性的名称为“value”,来源于 JMSTextBody 的模式定义(在 JMSCustomBindingLibrary/JMSBodyModels.xsd 文件中):

清单 4.
  <xsd:complexType name="JMSTextBody">
         <xsd:sequence>
            <xsd:element name="value" type="xsd:string"/>
          </xsd:sequence>
  </xsd:complexType>

在该中介的 WSDL 接口中引用 JMSTextBody 类型。清单 2 中的 portType 消息指向这个模式。换句话说,该中介的接口定义了数据绑定类必须处理的 SDO 的结构。因此,任何 JMS 自定义数据绑定实现都与关联于中介流组件接口的模式密切相关!

其他 JMS 消息类型的数据绑定类也是类似的。我们将把对这些类的分析工作留给您完成。此外,每个数据绑定实现都与 JMSBodyModels.xsd 文件中给出的相应模式定义密切相关。JMSStreamDataBinding 和 JMSMapDataBinding 类稍微复杂一点,因为它们需要保留流或映射中每个单独的元素的类型信息。有关更详细的信息,请查看 JMSBodyModels.xsd 文件中的复杂类型定义。

所有自定义 JMS 数据绑定都具有一个称为 AbstractJMSDataBindingImpl 的公共超类,这个类实现了一些公共的功能,比如,进行异常处理的方法。

JMSDataBindingLogger 类负责进行日志记录。要在自定义绑定代码中激活日志记录,可以将 WebSphere ESB 运行时中的 Trace details 设置为 com.ibm.websphere.sibx.samp.jms.*=fine

JMS 参数

完成 JMS 自定义绑定定义的最后一步是为传入和传出的流量选择合适的 JMS 资源。

在绑定属性中,还定义了所需的 JMS 队列目标和连接工厂引用。如下面的图中所示,JMS 导出绑定组件的连接工厂的定义位于 JMS Export Binding 选项卡的高级设置下面:

图 4. JMS 导出绑定的连接工厂引用定义
图 4. JMS 导出绑定的连接工厂引用定义
图 4. JMS 导出绑定的连接工厂引用定义

队列目标引用的定义位于 JMS Destinations 选项卡下面:

图 5. JMS 导出绑定的队列目标引用定义
图 5. JMS 导出绑定的队列目标引用定义
图 5. JMS 导出绑定的队列目标引用定义

我们没有给出导入绑定组件相应的屏幕截图,因为除了可以在 JMS Import Binding 选项卡下面找到连接工厂定义之外,它们基本上都是相同的。在您导入本文中的项目交换文件时,所有这些值都应该已经设置好了。

中介流

在为导出和导入编写了自定义绑定代码之后,我们可以开始将注意力集中到中介流组件本身。在 WebSphere Integration Developer Assembly Editor 中,打开 JMSCustomBindingMediationModule,然后双击 JMSCustomBindingMediationComponent1 以打开 Mediation Flow Editor。在这个编辑器中,流组件接口的每项操作都表示为一个请求和一个响应流(图 6)。

图 6. Mediation Flow Editor
图 6. Mediation Flow Editor
图 6. Mediation Flow Editor

这些流完全与导入和导出中所使用的那些绑定独立。实际上,这正是在流实现之外将其转换为一个 SDO DataObject 实例的原因:无需知道发送到该中介模块的和该中介模块发送的消息的协议和格式,就可以构建这些流。

在这里,我们只将其中的一个流作为示例进行详细描述,即 JMS TextMessage 流,因为这是最常见的 JMS 消息类型。我们还为这个消息类型的响应流添加了一些特定的异常处理机制。(其他消息类型的流也都是非常相似的。)

让我们一步一步地描述该中介模块对 JMS TextMessage 的处理:

  1. 当客户端向中介模块发送 JMS TextMessage 时,中介模块将这个消息放到 JMS 导出绑定组件的接收队列目标中。因为这个导出组件作为 JMS 自定义绑定实现,所以由自定义的函数选择器对该消息进行处理。如上所述,根据传入消息的类型,选择器返回函数名称“handleText”。

  2. 由于导出组件采用方法级数据绑定定义(如图 2 所示),所以选择了 JMSTextDataBinding 类来处理数据转换。这个类提取了传入的 JMS TextMessage 的文本有效负载,并且创建了遵循适当的 XML 数据类型(即 JMSTextBody)的 SDO DataObject 实例。将 DataObject 中的“value”字符串属性设置为 JMS TextMessage 的有效负载的值。

  3. 然后将这个新的 DataObject 传递给中介流组件。因为将它传递给了 handleText 操作,所以这个流处理的消息正是当您在 Mediation Flow Editor 中选择 handleText 链接时所看到的那个消息(请参见图 6)。

  4. 使用 MessageLogger 中介基元在请求流中记录下这个消息。

  5. 当这个消息离开请求流时,它进入在 Mediation Flow Editor 中由 JMSCustomBindingPartnerInterface 表示的 JMS 导入绑定组件。在这里,将 DataObject 重新转换为 JMS TextMessage,并且最终发送到导入组件的发送队列目标。

  6. 服务提供者(在我们的测试应用程序中是消息驱动的 Bean)监听这个队列目标上的消息。它的 onMessage() 方法仅打印出接收到的消息的内容。其中添加了一些特殊的代码,以模拟异常处理过程:如果文本消息的内容为“exception”,那么将该响应 JMS TextMessage 中称为 IsBusinessException 的 Header 属性设置为“true”。

  7. 现在,将响应 JMS TextMessage 发送回该中介模块,并将对其进行和前面一样的处理:JMS 导入绑定组件将其转换为 SDO DataObject。如果传入 IsBusinessException 属性设置为“true”的 TextMessage,那么通过错误调出 错误调出 而不是正常的响应调出 正常调出 将它传递给响应流。

  8. 如图 6 所示,在响应流中,如果通过错误调出 错误调出 输入消息,那么仅对这个消息进行记录(通过 MessageLogger3),并将其转发到错误输入 错误输入。在所有其他的情况下,消息将通过一个称为 BusinessExceptionCustomMediation 的自定义中介元素,我们在这个中介元素中添加了相应的逻辑,以便在文本消息的内容为“Error”时生成另一个异常。在这种情况下,该消息将流过自定义中介元素的失败终端,并对其进行记录,然后再次被转发到错误输入 错误输入

  9. 对于所有“正常”消息(即没有执行异常处理的消息),仅对其进行记录,并转发到输入终端 输入终端

  10. 最后,将到达 JMS 导出组件的响应 DataObject 重新转换为 JMS TextMessage,并将其放到发送队列目标。这样,任何 JMS 客户端都可以对其进行检索。如果导出组件通过中介流的错误输入 错误输入 接收到响应消息,那么它不仅将执行正常的数据转换,还将为生成的 JMS TextMessage 添加一个名为 IsBusinessException 的 Header。客户端可以使用它来确认在消息处理的过程中是否发生了异常。

将其组合到一起

既然我们已经完成了导出和导入绑定的定义,并且提供了中介流的实现,那么我们就可以最终部署并运行整个示例了。在第 2 部分中,我们向您介绍了如何安装和部署测试应用程序客户端和测试提供程序。还需要进行安装的就只有该中介模块了。

有两种方法可以将中介模块安装到 WebSphere ESB 运行时服务器:

  1. 使用 WebSphere Integration Developer WebSphere ESB 测试环境:

    1. 在 WebSphere Integration Developer 中,打开 Servers 视图,请确保创建并启动了 WebSphere ESB Server v6

    2. 右键单击该服务器并从上下文菜单中选择 Add and remove projects…

    3. 在接下来的对话框中,选择 JMSCustomBindingMediationModuleApp 项目,并将它添加到服务器。

  2. 使用 WebSphere ESB 管理控制台:

    1. 在 WebSphere Integration Developer 中,切换到 J2EE Perspective 并选择 Enterprise Applications 下的 JMSCustomBindingMediationModuleApp

    2. 将该项目导出到一个 EAR 文件。

    3. 打开 Servers 视图,右键单击 WebSphere ESB Server v6 并从上下文菜单中选择 Run administrative console

    4. 在管理控制台中,通过 Applications => Install New Application 将导出的 EAR 安装到运行时中。

要测试该应用程序,可以打开一个 Web 浏览器,然后浏览至 http://<HOSTNAME>:<HOSTPORT>/JMSTestClientWeb/index.html(<HOSTNAME> 和 <HOSTPORT> 取决于您本地的服务器配置,通常可以使用“localhost”和端口 9080)。请按照第 2 部分中给出的说明来发送不同类型的 JMS 消息,然后选择 Click here to select messages on the reply queue 以查看响应消息。其屏幕显示应该与图 7 所示类似。

图 7. 成功发送一个 TextMessage 后 JMSTestClient 的结果 Web 页面
图 7. 成功发送一个 TextMessage 后 JMSTestClient 的结果 Web 页面
图 7. 成功发送一个 TextMessage 后 JMSTestClient 的结果 Web 页面

结束语

在本文中,我们通过一个具体的示例向您介绍了如何在 WebSphere ESB 中为中介流组件构建 JMS 自定义绑定。我们讨论了处理传入和传出的 JMS 消息所必需的数据绑定和函数选择器代码,以及导出和导入所需的绑定属性。我们了解了如何以可视化的方式构建中介流组件,最重要的是,独立于与中介流进行通信的过程中使用的协议。

我们以这篇文章结束了关于这个主题的系列文章。请继续阅读更多关于 WebSphere ESB 的文章,以及如何使用它及其相应的工具 WebSphere Integration Developer 来构建高级的解决方案!

本系列的其他文章


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere, SOA and web services, Java technology
ArticleID=151807
ArticleTitle=IBM WebSphere 开发者技术期刊: 使用 JMS 和 WebSphere ESB 构建强大而可靠的 SOA——第 3 部分
publish-date=08032006