内容


ESB 实践

在 WebSphere Application Server V6 中创建企业服务总线

Comments

引言

通过 IBM 的产品和其他中间件供应商的产品实现 ESB 有多种方式。IBM WebSphere Application Server V6 为构建 ESB 提供了非常有意义的平台。[Reinitz1] 介绍了 V6 中提供的 WebSphere Messaging Resources(也称为 SIBus)。SIBus 可以用于构造多种 ESB 拓扑。文章中介绍了一些概念和术语,而 [Reinitz2] 描述了允许 SIBus 与 SOAP/HTTP Web 服务交互所需的设置。您还可以从 WebSphere Application Server V6 InfoCenter 中的服务集成标题下获得关于 SIBus 的更多信息。

ESB 中最常提到的两个功能是消息转换和消息路由。本文以 [Reinitz1] 和 [Reinitz2] 中介绍的技术为基础,演练了如何创建执行消息转换和和路由的 ESB 的实例。

图 1 概括了我们将要实现的场景。该图标识了服务请求者,服务请求者可能通过遵循 JAX-RPC 的代理,使用提供具有所显示签名的发布操作的服务。该图还标识了提供所显示签名的通知操作的服务提供者。可以看到,服务请求者需要的操作与实际的服务提供者可以提供的操作不匹配。 我们要构建的 ESB 角色需要执行所需的消息转换,这样服务请求者就可以与服务提供者进行成功通信。更确切地说,我们构建的 ESB 必须执行 WSDL portType 映射。

图 1. PortType 映射场景
PortType 映射场景
PortType 映射场景

解决方案分析

首先,让我们分析场景以了解必须创建哪一类 SIBus(以下称为 bus)拓扑。从最初的分析开始,我们必须:

  1. 将 SOAP/HTTP 请求从请求者发送到总线。为此,我们需要使用请求者的 WSDL 在总线创建入站服务。要定义入站服务,我们首先需要安装 SOAP/HTTP 端点侦听器应用程序,如 [Reinitz2] 中所述。
  2. 将请求从请求者的形式转换成提供者的形式。这就要求目的地的中介允许消息处理(在该场景中指消息转换)。我们必须创建和安装中介或者中介处理程序列表来执行转换;在该场景中,我们将使用 XSLT 执行转换。
  3. 将请求从总线发送到提供者。为此,我们需要使用提供者的 WSDL 创建出站服务。定义出站服务的前提条件是安装 SOAP/HTTP 服务调用程序,它安装在端点侦听器上。
  4. 将响应从提供者的形式转换成请求者的形式。即使这两种形式的响应是无效的,响应也是必需的。响应具有的名称空间的差异必须要进行处理。由于没有真正的内容,因此我们要模拟出适合于请求者的硬编码消息插入到响应消息中。即使这需要创建和安装中介处理程序列表来处理响应。

这一分析向我们提供了我们需要做的工作的情况。如果根据对总线的更加具体的了解进行更深入的分析,我们将受益匪浅。上面的第一步需要我们创建入站服务。如图 2 中所示,入站服务包括将消息路由到入站服务目的地(用黄色矩形表示)的端点侦听器。入站服务目的地必须在创建入站服务之前出现。当我们创建入站服务时,图中所示的应答目的地将自动创建。该应答目的地用于通过端点侦听器将应答路由回请求者。对于使用应用程序服务器管理控制台创建的所有入站服务来说,只有一个缺省的应答目的地。

创建入站服务的这些事实意味着我们必须对入站服务目的地和应答目的地作出几个设计决策。例如,我们在创建入站服务时使用哪一个目的地,以及我们在哪(在哪个目的地)转换响应消息?在回答这些问题之前,我们需要学习出站服务的创建。

图 2. 入站服务
入站服务
入站服务

第 3 步要求我们创建出站服务。通过自动创建出站服务,可以创建表示服务定义中的服务和每一个端口的目的地,并将它们连接到服务调用程序,如图 3 所示。此外,通过自动创建出站服务,还可以将应答从服务调用程序路由到缺省的应答目的地。

图 3. 出站服务
出站服务
出站服务

我们有两种选择来创建入站服务;我们可以重用为我们创建的出站服务目的地,也可以为入站服务创建新的目的地来接收消息,并将这些消息路由到出站服务目的地。这两种选择都是可行的;但是,在我们的示例中,没有必要引入另一个目的地,因此我们将重用出站服务目的地。然而,这意味着在创建入站服务之前,我们需要创建出站服务。

要完成第 4 步,我们需要将目的地作为中介来转换应答消息。由于我们要在管理控制台创建总线拓扑,因此该拓扑将使用一个总线范围内的缺省应答目的地。在我们的简单场景中,可以将缺省应答目的地作为中介。然而,在实际环境中,这是不可行的,因为所有入站服务的全部应答都要通过这个缺省的应答目的地。因此,在应答消息到达缺省应答目的地并最后回到请求者之前,我们要在应答路径中插入应答截获目的地来截获和转换来自出站服务的应答消息。

最后结果是我们将创建图 4 中所示的运行时拓扑。首先,我们要创建出站服务,它自动创建出站服务目的地和出站端口目的地。然后,我们创建入站服务,将出站服务目的地作为入站服务的目标目的地。可以将出站服务目的地作为中介来转换请求消息(中介由蓝色六边形表示)。我们需要手动创建应答截获目的地。可以将应答截获目的地作为中介来转换响应。创建应答截获中介不会影响响应从服务调用程序到缺省应答目的地的缺省路由,了解这一点是非常重要的。要强制应答首先到达应答截获目的地,我们需要修改作为请求消息上下文的一部分的反向路由路径。我们要在出站服务目的地中介中修改反向路由路径。

图 4. 运行时拓扑
运行时拓扑
运行时拓扑

结果是一样的,重要的是我们需要作出的决策。我们可以在现有的 SIBus 实例中构建图 4 中的拓扑,也可以创建新的实例。对于这个简单的 ESB 场景,我们将创建新的总线,因为我们不需要与其他的总线构件交互。

我们已经完成了分析,现在可以开始构建了!对于本文,我们将执行在 Rational V6 开发工具(Application Developer 或 Software Architect)提供的 WebSphere 测试环境中实现解决方案所需的所有步骤。这些工具使您能够使用功能齐全的 WebSphere Application Server v6,并利用集成开发环境的所有好处。在本例中,我们将使用工具来创建图 4 中的拓扑、开发中介处理程序,以及测试所有内容。本文假定您熟悉 Rational 工具,我们没有介绍非特定于 SIBus 的开发实践的细节。

创建出站服务

在创建出站服务之前,我们需要创建新的总线实例,称为 TypeMap。有关使用管理控制台创建新的总线实例的步骤描述,请参阅 [Reinitz1]

要创建出站服务,需要向管理控制台提供描述服务提供者的 WSDL 文档。清单 1 显示了名为 SubscriberS1T1Service 的服务(portType 为 SubscriberS1T1)的 WSDL 文件的重要代码片段。它遵循根据包装文档的文本形式定义的最佳实践。它还遵循导入参数的模式(而不是直接嵌入模式)的最佳实践。清单 2 显示了这一模式。

清单 1. SubscriberS1T1Service 的 WSDL
<wsdl:definitions xmlns:impl="http://pubsubhub.ibm.com"
    xmlns:intf="http://pubsubhub.ibm.com" ... >
 <wsdl:types>
  <schema xmlns:sub="http://www.sub1.com/sub1doc" ... >
   <import namespace="http://www.sub1.com/sub1doc" 
      schemaLocation="sub1Topic1.xsd"/>
   <element name="notify">
    <complexType>
       <sequence>
        <element name="doc" nillable="true" type="sub:Sub1Topic1Doc"/>  
       </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>
 <wsdl:message name="notifyRequest">
      <wsdl:part element="impl:notify" name="parameters"/>
 </wsdl:message>
 <wsdl:portType name="SubscriberS1T1">
      <wsdl:operation name="notify">
         <wsdl:input message="impl:notifyRequest" 
        name="notifyRequest"/>
         <wsdl:output message="impl:notifyResponse" 
        name="notifyResponse"/> 
      </wsdl:operation>
   </wsdl:portType>
  ...
 <wsdl:service name="SubscriberS1T1Service">
      <wsdl:port binding="impl:SubscriberS1T1SoapBinding" 
    name="SubscriberS1T1">
         ...
      </wsdl:port>
 </wsdl:service>
清单 2. SubscriberS1T1Service 的模式
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.sub1.com/sub1doc" ... >
  <complexType name="Sub1Topic1Doc">
    <sequence>
      <element name="firstName" type="string"/>
      <element name="lastName" type="string"/>
    </sequence>
  </complexType>
  <element name="Sub1Topic1Doc" 
     type="tns:Sub1Topic1Doc"></element>
</schema>

要创建出站服务,请执行以下步骤:

  1. 在管理控制台,选择 Service integration => Buses => TypeMap => Outbound Services => New
  2. 在 New Outbound Service 对话框中,确定定义服务提供者(由出站服务表示)的 WSDL 文档的位置。我们将提供一个 URL,如下所示。
  3. 单击 Next
    图 5. 创建出站服务,第 1 步
    创建出站服务,第 1 步
    创建出站服务,第 1 步
  4. 在下一个对话框中,需要选择一个服务,因为可能存在文档中定义的多个服务。SubscriberS1T1Service 的 WSDL 只包含一个服务,因此我们将使用缺省选项。单击 Next
  5. 在 Select Ports 对话框中,选择发送消息的端口。同样,虽然可能存在多个端口,但是 SubscriberS1T1Service 只包含一个端口,它应该被默认选择。单击 Next
    图 6. 创建出站服务,第 3 步
    创建出站服务,第 3 步
    创建出站服务,第 3 步
  6. 在图 7 中所示的 Name the Outbound Service and Destinations 对话框中,请执行以下步骤:
    1. 输入图中所示的 Outbound service name。这是一个方便您使用的管理名称,可以是您希望的任何名称,但是它在总线实例中必须是唯一的。
    2. 输入 Service destination name。管理控制台创建的缺省名称是 WSDL 目标名称空间和服务名称的组合。在本例中,我们将使用这一缺省名称。
    3. 最后,在 Port destination name 字段中输入出站端口目的地的名称。同样,管理控制台将选择缺省名称,它们是 WSDL 目标名称空间、服务名称和端口名称的组合。唯一端口的名称适用于本示例。
    4. 单击 Next
      图 7. 创建出站服务,第 4 步
      创建出站服务,第 4 步
      创建出站服务,第 4 步
  7. 在下一个对话框中,单击 Finish

现在我们已经创建了出站服务。在这一步骤以及创建图 4 中所示的拓扑的每一个重要步骤之后保存配置,这是一个很好的想法。

创建入站服务

要定义入站服务,需要向管理控制台提供描述服务请求者的接口预期的 WSDL 文档。清单 3 显示了名为 PublisherS1T1Service 的服务(portType 为 PublisherS1T1)的 WSDL 文件的重要代码片段。它遵循根据包装文档的文本形式定义的最佳实践。它还遵循导入参数的模式(而不是直接嵌入模式)的最佳实践。清单 4 显示了这一模式。通过比较 SubscriberS1T1Service 的 WSDL 和 PublisherS1T1Service 的 WSDL,可以看出我们需要转换请求消息的两个方面的内容:操作和主体,其中包括名称空间。

清单 3. PublisherP1T1Service WSDL
<wsdl:definitions xmlns:impl="http://pubsubhub.ibm.com" 
    xmlns:intf="http://pubsubhub.ibm.com" ... >
 <wsdl:types>
  <schema xmlns:sub="http://www.sub1.com/sub1doc" ... >
    <import namespace="http://www.pub1.com/pub1doc" 
        schemaLocation="pub1Topic1.xsd"/>
 <element name="publish">
    <complexType>
       <sequence>
        <element name="doc" nillable="true" type="sub:Pub1Topic1Doc"/>  
       </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>
<wsdl:message name="publishRequest">
      <wsdl:part element="impl:publish" name="parameters"/>
 </wsdl:message>
 <wsdl:portType name="PublisherS1T1">
      <wsdl:operation name="publish">
         <wsdl:input message="impl:publishRequest" name="publishRequest"/>
         <wsdl:output message="impl:publishResponse" name="publishResponse"/> 
      </wsdl:operation>
  </wsdl:portType>
  ...
   <wsdl:service name="PublisherP1T1Service">
      <wsdl:port binding="impl:PublisherP1T1SoapBinding" name="PublisherP1T1">
         ...
      </wsdl:port>
  </wsdl:service>
清单 4. PublisherP1T1Service 模式
<?xml version="1.0" encoding="UTF-8"?>
<schema 
 targetNamespace="http://www.pub1.com/pub1doc" ... >
  <complexType name="Pub1Topic1Doc">
    <sequence>
      <element name="firstName" type="string"/>
      <element name="lastName" type="string"/>
      <element name="address" type="string"/>
   </sequence>
  </complexType>
  <element name="Pub1Topic1Doc" type="tns:Pub1Topic1Doc"></element>
</schema>

要创建入站服务,请执行以下操作:

  1. 在管理控制台,选择 Service integration => Buses => TypeMap => Inbound Services => New
  2. 在 New Inbound Service 对话框中,需要确定入站服务目的地和定义服务请求者的预期的 WSDL 文档的位置。如上所述,我们将选择在创建出站服务期间自动创建的出站服务目的地,并为 WSDL 提供一个 URL,如下所示。
  3. 单击 Next
    图 8. 定义入站服务,第 1 步
    定义入站服务,第 1 步
    定义入站服务,第 1 步
  4. 在下一个对话框中,需要选择服务。PublisherS1T1Service 的 WSDL 只包含一个服务,因此我们将使用缺省选项。单击 Next
  5. 您将看到图 9 中所示的对话框,请执行以下操作:
    1. 输入 Inbound service name。这是一个方便您使用的管理名称,可以是您希望的任何名称。管理控制台将选择一个缺省名称,它是有关绑定的信息、目的地名称和其他名称的组合;该缺省名称是唯一的,但是通常很长而且很难理解。在我们的示例中,我们将使用服务的名称。
    2. 选择端点侦听器,然后单击 Next
      图 9. 定义入站服务,第 3 步
      定义入站服务,第 3 步
      定义入站服务,第 3 步
    3. 在下一个对话框中,单击 Finish

现在我们已经创建了入站服务。

一点修正

由于 Application Server V6.0.1 以及早期版本中的限制,在 WSDL 文档中使用参数的单独模式的最佳实践将导致在运行时才可以发现的问题。管理控制台自动将 WSDL 文档加载到 SIBus 使用的 SDO 资源库来处理 Web 服务消息。(有关 SDO 资源库的详细信息,请参阅 [Reinitz2]Application Server InfoCenter。)然而,它不能将 WSDL 文档引用的各个模式加载到资源库。这意味着我们需要使用命令行工具 wsadmin 来手动加载模式。所用的 wsadmin 命令的一般形式为:

set sdoRepBean [$AdminControl queryNames *:*, type=SdoRepository]
puts [$AdminControl invoke $sdoRepBean importResource 
{"resourceName" "sourceFileName"}]

首先,打开命令行并转到目录 <install_path>/profiles/<profile>/bin,其中 <install_path> 指的是安装 Application Server 的路径,而 <profile> 指的是要使用的概要信息(可能是缺省的)。键入 wsadmin 启动工具;然后输入上面所示的设置命令。下列命令用于通过文件名加载两个服务的 schema:

puts [$AdminControl invoke $sdoRepBean importResource {"pub1Topic1.xsd"
"C:/workspace/MyServices/WebContent/wsdl/com/ibm/pubsubhub/pub1Topic1.xsd"}]
puts [$AdminControl invoke $sdoRepBean importResource {"sub1Topic1.xsd" 
"C:/workspace/MyServices/WebContent/wsdl/com/ibm/pubsubhub/sub1Topic1.xsd"}]

最好使用一个 URL 来查找 schema,但是上面所用的文件系统的位置也许是可用的。您只需要一次发出上面的命令。如果需要确定 SDO 资源库中是否已有某些内容,可以使用下列命令:

puts [$AdminControl invoke $sdoRepBean listResources].

创建截获应答目的地

我们需要创建截获应答的目的地,以便消息可以进行相应地转换。为此,请完成以下步骤:

  1. 在管理控制台,选择 Service integration => Buses => TypeMap => Destinations => New
  2. 在 Create New Destination 对话框中,请确保选择 Queue,然后单击 Next
    图 10. 创建目的地
    创建目的地
    创建目的地
  3. 在 Create New Queue 对话框中,在 Identifier 字段中输入 FixReply。添加可选描述,然后单击 Next
    图 11. 创建目的地,第 1 步
    创建目的地,第 1 步
    创建目的地,第 1 步
  4. 在下一个对话框中,必须将队列分配给总线成员。由于我们的场景中只有一个队列,因此只需单击 Next
  5. 在下一对话框中,单击 Finish

现在我们已经创建了截获目的地。

将入站服务目的地作为中介

现在我们需要将传入消息从适合 PublisherP1T1Service 的形式转换成适合 SubscriberS1T1Service 的形式。为此,我们将入站服务目的地作为中介,如图 4 所示。中介包括一组中介处理程序;在我们的示例中,该列表只包含一个本部分中描述的中介处理程序。我们必须使用中介处理程序来将消息从一种形式转换成另一种形式。由于这需要 XML 到 XML 的转换,因此中介处理程序将使用 XSLT 进行实际的转换。[Reinitz3] 提供了关于编写和部署中介的详细信息。

XSLT 必须做两件事情。首先,它必须处理消息周围的 SOAP 包装程序;这意味着忽略传入消息周围的包装程序,并在传出消息周围插入包装程序。其次,它必须转换消息的主体;这意味着将名称空间 http://www.pub1.com/pub1doc 中由类型 Pub1Topic1Doc 描述的文档转换成名称空间 http://www.sub1.com/sub1doc 中由类型 Sub1Topic1Doc 描述的文档。Rational 工具包括 XML 到 XML 的映射工具,它可以帮助创建 XSLT,如下所示。

清单 5. 用于转换请求消息的 XSLT
<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform
    version="1.0" xmlns:xalan=http://xml.apache.org/xslt
    xmlns:sub1="http://www.sub1.com/sub1doc" 
    xmlns:pub1="http://www.pub1.com/pub1doc" 
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:q0="http://pubsubhub.ibm.com" exclude-result-prefixes="pub1">
  <xsl:output method="xml" encoding="UTF-8" indent="yes" 
      xalan:indent-amount="2" /> 
  <xsl:strip-space elements="*" /> 
  <xsl:template match="soapenv:Envelope/soapenv:Body/q0:publish">
    <soapenv:Envelope 
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:q0="http://pubsubhub.ibm.com" 
  xmlns:q1="http://www.sub1.com/sub1doc" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:tns="http://www.sub1.com/sub1doc" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
        <q0:notify>
          <q0:doc>
            <sub1:firstName>
              <xsl:value-of select="pub1:doc/pub1:firstName/text()" /> 
            </sub1:firstName>
            <sub1:lastName>
              <xsl:value-of select="pub1:doc/pub1:lastName/text()" /> 
            </sub1:lastName>
          </q0:doc>
        </q0:notify>
      </soapenv:Body>
    </soapenv:Envelope>
  </xsl:template>
 </xsl:stylesheet>

中介处理程序运行在 EJB 容器中。我们将使用 Rational 工具来开发和部署中介处理程序,以将其用于总线中。

首先创建 EJB 项目。在该项目中,创建名为 P1S1Transform 的 Java 类(而不是 EJB!),它扩展接口 com.ibm.websphere.sib.mediation.handler.MediationHandler。清单 6 显示了用于转换消息的名为 P1S1Transform 的中介处理程序的框架。它首先获取两个上下文属性,这两个属性将在入站/出站服务目的地进行设置;这些上下文属性将在目的地进行设置,并允许根据其运行的目的地自定义中介的行为。属性 traceOn 使中介处理程序能够在日志中写入信息(或者运行在控制台的开发工具中)。属性 xsltName 向中介处理程序提供包含 XSLT 定义的文件的名称。

清单 6. P1S1Tranform 中介处理程序
public class P1S1Transform implements MediationHandler {
	protected String xsltName = null;
	protected boolean traceOn = false;
	protected SIMessageContext siMC = null;
	protected SIMessage message = null;
	protected String defaultOutputFormatString = SIApiConstants.JMS_FORMAT_BYTES;
	public boolean handle(MessageContext messageContext) throws
     MessageContextException {
		String mediationName = "P1S1Transform";
		// get the needed context properties
		traceOn = ((Boolean)messageContext.getProperty("traceOn")).booleanValue();
		xsltName = (String) messageContext.getProperty("xsltFileName");	
		if (traceOn) {
			System.out.println("**** Entering " + mediationName
			 + " Mediation *****");
			System.out.println("on Destination - " + 
			   ((SIMessageContextProxyImpl) messageContext)
			   .getSession().getDestinationName());
			System.out.println("reading properties - traceOn, xsltFileName");
			System.out.println("traceOn is " + traceOn 
			+ "  xsltFileName is " + xsltName);
		}
		...
	}
}

清单 7 显示了消息转换的重要方面。首先获取字节数组形式的消息,它是 XSLT 转换所必需的输入。接下来,我们获取 XSLT 定义并使用 javax.xml.transform 来转换消息。这里没有展示使用转换器的细节。接着,使用转换的消息替换当前的消息。然后,我们将入站服务的消息的格式改为出站服务的消息的格式。这一步值得进行一些说明。

清单 7. P1S1Tranform 中介处理程序,转换代码
// Convert the MessageContext into an SIMessageContext
	siMC = (SIMessageContext) messageContext;
	// Retreive the message from the session
	message = siMC.getSIMessage();
		
	// Transform the message 
	try {
		DataGraph graph = 
            message.getNewDataGraph(SIApiConstants.JMS_FORMAT_BYTES);
		DataObject body = graph.getRootObject();
		if (body.isSet("data")) {
			// Grab the bytes
			byte[] payload = body.getBytes("data/value");
			// Transform the bytes from P1 format to S1 format
			String transform = readFileAsString(xsltName);
			payload = XSLTTransform.transform(payload, transform);
		       // Replace the payload in the data graph
		       body.setBytes("data/value",payload);
		        
		       // Replace the contents of the message with the new graph
		       String outputFormatString = 
"SOAP:dest:TypeMap:http://pubsubhub.ibm.com:SubscriberS1T1Service,
http://pubsubhub.ibm.com,SubscriberS1T1Service,SubscriberS1T1";
		       message.setDataGraph(graph, SIApiConstants.JMS_FORMAT_BYTES);
		       DataGraph newGraph = message.getNewDataGraph(outputFormatString);
		       message.setDataGraph(newGraph, outputFormatString);
			
		} else {
			...
		}
			
		return true;
	} catch (Exception e) {
		...
	}

总线使用格式字符串解析消息的格式。有关详细信息,请参阅 Application Server InfoCenter。例如,格式字符串确定定义消息的 WSDL 和模式。可以将格式字符串看作指向 SDO 资源库的指针。总线使用格式字符串生成 SDO,中介可以使用该 SDO 来检查和更改消息。对于 SOAP 消息,格式字符串的语法为:

SOAP:<SDO repository key>,<service namespace>,<service name>,<port name>


入站/出站服务的 <SDO repository key> 的格式为:

in:<busname>:<inbound service name>
dest:<busname>:<service destination name>


因此,为了将 SOAP 消息的格式转换为出站服务的适当格式,P1S1Transform 必须使用格式字符串:

SOAP:dest:TypeMap:http://pubsubhub.ibm.com:SubscriberS1T1Service,
http://pubsubhub.ibm.com,SubscriberS1T1Service,SubscriberS1T1

请记住,我们必须修改反向路由路径(应答或响应使用的路径),这样应答在进入缺省应答目的地之前就会进入应答截获目的地。清单 8 显示了完成此任务的代码。它放在清单 7 的代码段中的 catch 语句之前。该代码首先获取当前应答或者反向路由路径。然后,它将名为 FixReply 的应答截获目的地插入到路径中的缺省应答目的地之前。最后,它使用修改的路径替换消息的反向路由路径。

清单 8. P1S1Tranform 中介处理程序,路由应答
// set reply (reverse routing) path
List rrp = message.getReverseRoutingPath();
SIDestinationAddress fixup = 
   SIDestinationAddressFactory.getInstance().
      createSIDestinationAddress("FixReply", false);
// insert before default reply
rrp.add(0, fixup);
message.setReverseRoutingPath(rrp);

现在开发中介处理程序就完成了,我们需要部署中介处理程序来创建中介或者中介列表。为此,请完成以下步骤:

  1. 查找 EJB 项目的部署描述符并打开。
  2. 在部署描述符编辑器的底部,选择 Mediation Handlers 选项卡。
  3. 可以看到中介处理程序的空列表。单击 Add
  4. 在 Define Mediation Handler 对话框中,我们将输入中介处理程序的属性。对于我们的场景,两个重要的属性是 NameHandler 类。可以是任何名称,但是向其提供中介处理程序类的名称(无包装)是一个不错的想法。该类必须具有完全限定的类名。单击 Browse 并键入处理程序类名的前几个字母,然后从列表中选择该名称。
  5. 单击 Finish
    图 12. 在部署描述符中定义中介处理程序
    在部署描述符中定义中介处理程序
    在部署描述符中定义中介处理程序

    部署描述符编辑器自动将中介处理程序放在与中介处理程序同名的列表中;结果如图 13 所示。可以将处理程序放在其他列表中,但是在我们的场景中没有必要这样做。


    图 13. 部署的中介处理程序
    部署的中介处理程序
    部署的中介处理程序
  6. 保存部署描述符。请确保包含中介处理程序 EJB 项目的 EAR 已部署到服务器。

现在我们需要将中介处理程序列表报告给总线。为此,请完成以下步骤:

  1. 选择 Service integration => Buses => TypeMap => Mediations => New
  2. Mediation name 字段中输入名称。让该名称与中介处理程序列表的名称匹配是一个不错的想法。
  3. Handler list name 字段中,输入在部署的 EAR 中定义的中介处理程序列表的名称。
  4. 单击 OK
    图 14. 定义中介
    定义中介

接着,需要将入站/出站服务目的地作为中介。为此,请执行以下步骤:

  1. 选择 Service integration => Buses => TypeMap => Destinations
  2. 在 Destinations 对话框中,选择类型为 Web 服务的出站服务目的地,然后单击 Mediate
    图 15. 目的地
    目的地
    目的地
  3. 在 Mediate Destinations 对话框中,可以为目的地选择中介。由于此时只定义了一个中介,因此它已选中。单击 Next
    图 16. 选择中介,第 1 步
    选择中介,第 1 步
    选择中介,第 1 步
  4. 在下一个对话框中,选择中介运行的节点。在测试环境中,只有一个节点,因此只需单击 Next
  5. 最后一个对话框确认在前面的对话框中作出的选择。单击 Finish
  6. 如图 17 中的对话框所示,已将出站服务目的地作为中介(名为 P1S1Transform)。
    图 17. 确认中介
    确认中介
    确认中介

将应答截获目的地作为中介

请记住,我们需要转换响应和请求。在实际工作中,只需使用入站服务所需的响应替换实际的服务提供者中的响应。我们可以这样做是因为请求者和提供者中的响应都是无效的。我们将应答截获目的地 FixReply 作为中介。需要创建名为 P1VoidReply 的新的中介处理程序。对于这一场景,我们将创建与 P1S1Transform 在同一个 EJB 项目中的类,以使运行在应用程序服务器中的应用程序的数量最少。作为中介处理程序,P1VoidReply 与 P1S1Transform 具有相同的一般形式,但是只需要一个上下文属性,即 traceOn。P1VoidReply 访问应答消息的方式必须与 P1S1Transform 访问请求消息相同。

清单 9 展示了执行替换消息任务的 P1VoidReply 的代码片段。处理程序只需使用字符串 newBody 替换应答主体,如下所示。最后,新的主体必须使用入站服务的适当格式存储;可以看到用于完成该任务的格式字符串。格式字符串遵循上面描述的用于入站服务的语法。

清单 9. FixReply 中介处理程序
   try {
	String newBody = "<env:Envelope 
  xmlns:ns1=\"http://pubsubhub.ibm.com\" 
  xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">
  <soapenv:Body xmlns:xsi=
  \"http://www.w3.org/2001/XMLSchema-instance\" 
  xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
  xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" 
  xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
  <p895:publicshResponse 
  xmlns:p895=\"http://pubsubhub.ibm.com\"/>
  </soapenv:Body></env:Envelope>";
	// Replace the contents of the message with the new graph
	DataGraph newGraph = 
    message.getNewDataGraph(SIApiConstants.JMS_FORMAT_TEXT);
	    newGraph.getRootObject().set("data/value", newBody);
message.setDataGraph(newGraph, SIApiConstants.JMS_FORMAT_TEXT);
// get the message represented as a SOAP message 
	String format = 
	"SOAP:inport:TypeMap:PublisherP1T1Service:SOAPHTTPChannel1InboundPort,
http://www.ibm.com/websphere/sib/webservices/flurryNode01Cell/TypeMap/Service,
PublisherP1T1Service,SOAPHTTPChannel1InboundPort";
	DataGraph soap = message.getNewDataGraph(format);
	message.setDataGraph(soap, format);
   } catch (Exception e) {
	...
   }
   return true;

设置上下文属性

请记住,我们已经将中介处理程序编码为依赖于某些必须在作为中介的目的地设置的上下文属性。如果中介处理程序试图获取没有定义的上下文属性,则您可能会遇到难以诊断的运行时故障。

要设置上下文属性,请执行以下步骤:

  1. 选择 Service integration => Buses => TypeMap => Destinations=>destination_name=>Context Properties=>New
  2. 输入名称、类型和值,然后单击 OK。 图 18 展示了为出站服务目的地定义上下文属性 traceOn 和 xsltFileName 的结果。
    图 18. 出站服务目的地的上下文属性
    出站服务目的地的上下文属性
    出站服务目的地的上下文属性

    图 19 展示了为应答截获目的地定义上下文属性 traceOn 的结果。
    图 19. 应答截获目的地的上下文属性
    应答截获目的地的上下文属性
    应答截获目的地的上下文属性

测试总线场景

在完成以上部分中描述的所有设置之后,请确保保存配置并重新启动服务器,以使配置更改生效。

Rational 开发环境是进行测试的一个重要的地方。可以利用工具中提供的所有调试功能,如断点、检查变量等等。可以通过作为 Rational 工具的一部分的 WSDL 资源管理器创建在总线定义的入站服务的客户机,也可以通过 Web 服务客户机向导(也是 Rational 工具的一部分)来创建客户机。

可以使用通用语法 http://<server:port>/sibus/wsdl/<bus_name>/<inbound_service_name> 在总线访问入站服务的 WSDL。对于 Application Server V6.0.1 或者早期版本,应该通过用于定义入站服务的 WSDL 来驱动工具,并更改端点地址。这是由于对先前描述的外部模式的限制所引起的,它阻止工具使用总线自动创建的 WSDL 描述入站服务。

在测试和调试使用 WebSphere Messaging Resources 的场景时,还有非常重要的一点需要提示。如果将应用程序服务器的跟踪功能设置为 *=info: SIBMessageTrace=all,则可以在应用服务器概要的 trace.log 文件中看到一些有用的诊断信息。这些跟踪信息在检测无效的格式字符串时特别有用。

结束语

本文描述了如何使用 Application Server V6 Messaging Resources 来创建在 Web 服务上下文中执行转换和路由的简单的 ESB 场景。它还描述了设置所需的入站和出站服务以及所需的目的地和中介处理程序的细节。这一基础示例可以帮助您创建更加复杂的 ESB 场景。有关更加复杂的场景的详细信息,请参阅 IBM 红皮书 Patterns: SOA with an Enterprise Service Bus in WebSphere Application Server V6 来获得关于更加复杂的场景的讨论。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere, SOA and web services, Rational
ArticleID=97798
ArticleTitle=ESB 实践
publish-date=09212005