在第 1 部分中,我们介绍了企业服务总线 (ESB) 的概念,在第2 部分中,我们演示了如何建立测试应用程序客户端和提供
如果您还记得第 1 部分, 我们曾讨论过构成 WebSphere ESB 基础的编程模型,即服务组件体系结构 (SCA)。在这个体系结构中,不同类型的组件都被看作服务,并且可使用相同的方式进行访问。WebSphere ESB 中介流是这些组件类型中的一种。与任何 SCA 组件一样,可以通过中介流所提供的导出对其进行访问,并且中介流可以通过导入将消息转发到其他的外部服务。JMS 的特殊类型的导入和导出,称为JMS 绑定,允许用户指定绑定配置并编写自己的数据处理代码。中介流本身包含一系列的中介基元,在消息通过总线时,可以使用这些中介基元对消息进行相应的操作。
另外两篇 developerWorks 文章 Getting started with WebSphere Enterprise Service Bus and WebSphere Integration Developer 和 Developing custom mediations for WebSphere Enterprise Service Bus 将向您介绍创建中介模块的基本场景。在继续学习本文之前,最好先阅读(或至少是浏览)这些文章。
概括地说,最后的这篇文章将:
将所有的内容组合到一个中介模块中,您可以将这个中介模块部署到 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 { |
请注意 generateEISFunctionName() 方法如何返回关联于指定消息的函数(或操作,或方法)的名称。在我们的示例中,函数名称为“handle”加上该消息类型。例如,对于 JMS 文本消息,将返回“handleText”。换句话说,当中介接收到一个 JMS 文本消息时,会将它发送到 handleText() 方法。
在导出的绑定属性中,在 Connection 选项卡的高级设置下,对函数选择器进行了定义,如图 1 所示。
图 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 要求每个服务组件都具有一个接口,该接口可以是一个
清单 2. JMS 文本消息的 portType 操作
p<wsdl:portType name="JMSCustomBindingInterface"> |
在为导出或导入绑定定义了该接口之后,您需要提供处理 DataObject 和 JMS 之间转换的数据绑定类。它可以是针对整个导出/导入的一个类,或者每个操作一个类。我们已经在接口中定义了几项操作(每个 JMS 消息类型一项操作),因此我们定义了方法级的数据绑定,如图 2 所示。
图 2. 方法级数据绑定定义
每个数据绑定类都必须实现 com.ibm.websphere.sca.JMS.data.JMSDataBinding 接口。
在我们的测试应用程序中,共有 5 个 JMSDataBinding 实现:
您可以在 JMSCustomBindingClasses 项目中找到它们的源文件和其他的实用工具类。
一个数据绑定实现必须提供对下列方法的实现:
图 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"> |
其他 JMS 消息类型的数据绑定类也是类似的。我们将把对这些类的分析工作留给您完成。此外,每个数据绑定实现都与 JMSBodyModels.xsd 文件中给出的相应模式定义密切相关。JMSStreamDataBinding 和 JMSMapDataBinding 类稍微复杂一点,因为它们需要保留流或映射中每个单独的元素的类型
所有自定义 JMS 数据绑定都具有一个称为 AbstractJMSDataBindingImpl 的公共超类,这个类实现了一些公共的功能,比如,进行异常处理的方法。
JMSDataBindingLogger 类负责进行日志记录。要在自定义绑定代码中激活日志记录,可以将 WebSphere ESB 运行时中的 Trace details 设置为 com.ibm.websphere.sibx.samp.JMS.*=fine。
JMS 参数
完成 JMS 自定义绑定定义的最后一步是为传入和传出的流量选择合适的 JMS 资源。
在绑定属性中,还定义了所需的 JMS 队列目标和连接工厂引用。如下面的图中所示,JMS 导出绑定组件的连接工厂的定义位于 JMS Export Binding 选项卡的高级设置下面:
图 4. JMS 导出绑定的连接工厂引用定义
队列目标引用的定义位于 JMS Destinations 选项卡下面:
图 5. JMS 导出绑定的队列目标引用定义
我们没有给出导入绑定组件相应的屏幕截图,因为除了可以在 JMS Import Binding 选项卡下面找到连接工厂定义之外,它们基本上都是相同的。在您导入本文中的项目交换文件时,所有这些值都应该已经设置好了。
中介流
在为导出和导入编写了自定义绑定代码之后,我们可以开始将注意力集中到中介流组件本身。在 WebSphere Integration Developer Assembly Editor 中,打开 JMSCustomBindingMediationModule,然后双击 JMSCustomBindingMediationComponent1 以打开 Mediation Flow Editor。在这个编辑器中,流组件接口的每项操作都表示为一个请求和一个响应流(图 6)。
图 6. Mediation Flow Editor
这些流完全与导入和导出中所使用的那些绑定独立。实际上,这正是在流实现之外将其转换为一个 SDO DataObject 实例的原因:无需知道发送到该中介模块的和该中介模块发送的消息的协议和格式,就可以构建这些流。
在这里,我们只将其中的一个流作为示例进行详细描述,即 JMS TextMessage 流,因为这是最常见的 JMS 消息类型。我们还为这个消息类型的响应流添加了一些特定的异常处理
让我们一步一步地描述该中介模块对 JMS TextMessage 的处理:
然后将这个新的 DataObject 传递给中介流组件。因为将它传递给了 handleText 操作,所以这个流处理的消息正是当您在 Mediation Flow Editor 中选择 handleText 链接时所看到的那个消息(请参见图 6)。
使用 MessageLogger 中介基元在请求流中记录下这个消息。
当这个消息离开请求流时,它进入在 Mediation Flow Editor 中由 JMSCustomBindingPartnerInterface 表示的 JMS 导入绑定组件。在这里,将 DataObject 重新转换为 JMS TextMessage,并且最终发送到导入组件的发送队列目标。
服务提供者(在我们的测试应用 程序中是消息驱动的 Bean)监听这个队列目标上的消息。它的 onMessage() 方法仅打印出接收到的消息的内容。其中添加了一些特殊的代码,以模拟异常处理过程:如果文本消息的内容为“exception”,那么将该响应 JMS TextMessage 中称为 IsBusinessException 的 Header 属性设置为“true”。
现 在,将响应 JMS TextMessage 发送回该中介模块,并将对其进行和前面一样的处理:JMS 导入绑定组件将其转换为 SDO DataObject。如果传入 IsBusinessException 属性设置为“true”的 TextMessage,那么通过错误调出 而不是正常的响应调出 将它传递给响应流。
如图 6 所示,在响应流中,如果通过错误调出 输入消息,那么仅对这个消息进行记录(通过 MessageLogger3),并将其转发到错误输入 。 在所有其他的情况下,消息将通过一个称为 BusinessExceptionCustomMediation 的自定义中介元素,我们在这个中介元素中添加了相应的逻辑,以便在文本消息的内容为“Error”时生成另一个异常。在这种情况下,该消息将流过自定义中 介元素的失败终端,并对其进行记录,然后再次被转发到错误输入 。
对于所有“正常”消息(即没有执行异常处理的消息),仅对其进行记录,并转发到输入终端 。
最后,将到达 JMS 导出组件的响应 DataObject 重新转换为 JMS TextMessage,并将其放到发送队列目标。这样,任何 JMS 客户端都可以对其进行检索。如果导出组件通过中介流的错误输入 接收到响应消息,那么它不仅将执行正常的数据转换,还将为生成的 JMS TextMessage 添加一个名为 IsBusinessException 的 Header。客户端可以使用它来确认在消息处理的过程中是否发生了异常。
将其组合到一起
既然我们已经完成了导出和导入绑定的定义,并且提供了中介流的实现,那么我们就可以最终部署并运行整个示例了。在第 2 部分中,我们向您介绍了如何安装和部署测试应用程序客户端和测试提供程序。还需要进行安装的就只有该中介模块了。
有两种方法可以将中介模块安装到 WebSphere ESB 运行时服务器:
使用 WebSphere ESB 管理控制台:
要测试该应用程序,可以打开一个 Web 浏览器,然后浏览至 http://
图 7. 成功发送一个 TextMessage 后 JMSTestClient 的结果 Web 页面
结束语
在 本文中,我们通过一个具体的示例向您介绍了如何在 WebSphere ESB 中为中介流组件构建 JMS 自定义绑定。我们讨论了处理传入和传出的 JMS 消息所必需的数据绑定和函数选择器代码,以及导出和导入所需的绑定属性。我们了解了如何以可视化的方式构建中介流组件,最重要的是,独立于与中介流进行通 信的过程中使用的协议。
我们以这篇文章结束了关于这个主题的系列文章。请继续阅读更多关于 WebSphere ESB 的文章,以及如何使用它及其相应的工具 WebSphere Integration Developer 来构建高级的解决方案!