...

IBM WebSphere 4.0 MQSeries Integrator Carla Sadtler

by user

on
Category: Documents
87

views

Report

Comments

Transcript

IBM WebSphere 4.0 MQSeries Integrator Carla Sadtler
采用IBM WebSphere 4.0及IBM
MQSeries Integrator的自助应用程序
设计自助应用程序
选择运行时环境
通过实例进行学习
Carla Sadtler
Barry Procopio
Robert Rehms
ibm.com/redbooks
国际技术支持组织
采用 IBM WebSphere 4.0 和 IBM MQSeries
Integrator的自助应用程序
2002 年 2 月
注意!使用本资料和它支持的产品之前,必须阅读第 329 页“注意事项”中的一般信息。
第二版(2002年2月)
本版适用于支持 Windows NT 和 Windows 2000 的 IBM MQSeries 5.2.1、支持 Windows NT 和
Windows 2000 的 IBM WebSphere Application Server 高级版,以及支持 Windows NT 和 Windows
2000 的 IBM MQSeries Integrator 2.02。
提示:本书是根据产品的正式上市 (GA) 版本编写而成,可能不适用于已广泛投入市场的产品。
如欲了解有关最新信息,请查阅产品说明文档或本红皮书的后续版本。
请将您的意见寄往:IBM Corporation, International Technical Support Organization
Dept. HZ8 Building 662
P.O. Box 12195
Research Triangle Park, NC 27709-2195
当您发送信息给 IBM 后,即授予 IBM 非专有权,IBM 对于您所提供的任何信息,有权利以任何它
认为适当的方式使用或散发,而不必对您负任何责任。
© 版权所有 国际商用机器公司 2001 年。保留所有权利。
美国政府用户注意 — 限定权利文档 — 使用,复制或公开文件应受到
IBM 公司签订的GSA ADP时效合同(Schedule Contract)所规定条款的限制。
更改概要
本节将阐述对本红皮书此版本,以及先前版本中所做的技术更改。此版本可能还包含没
有指明的微小更正和编辑更改。
更改概要
SG24-6160-01
采用 IBM WebSphere 4.0 和 IBM MQSeries Integrator 的自助应用程序》
2002 年 2 月 8 日创建或更新。
2002年2月第2版
此次修订反映了如下描述的新信息及已更改信息的增加、删除或修改。
新信息
新增了“自助分解”应用模式,并采用 MQSeries Integrator2 聚集器插件 SupportPac
IA72 进行了实施。
以便使 WebSphere 应用程序能够接收和处理未经请求的 MQSeries 消息,在实例应
用程序中实施了 WebSphere Enterprise Services 提供的 JMS 侦听器功能。
已更改信息
更改了作为实例的应用程序。该应用程序的基础是 PDK-Lite3.0,您可从以下“电
子商务模式”网站下载:http://www.ibm.com/framework/patterns。
修改后的应用程序增加了消息发送功能。
该应用程序基于 WebSphere Application Server4.0 高级版、IBM MQSeries 5.2.1,以
及 IBM MQSeries Integrator 2.02。
iii
iv
前言
本书将重点探讨如何使用 IBM 电子商务模式定义的“路由器”应用模式和“分解”应
用模式,来设计和实施自助服务应用程序。
路由器应用模式采用集中星型(a hub-and-spoke)体系结构,提供从多个客户机到多
个后端应用程序的智能路由。用户和后端应用程序之间的交互是一对一的关系,即用户
一次一个地与应用程序进行交互。主要商业逻辑驻留在后端层。本书将介绍如何使用
IBM MQSeries 集成器和 IBM WebSphere 应用程序服务器来实施路由器型应用程序。
分解应用模式是对路由器应用模式的展开,它将介绍路由器应用模式的所有特性和功
能,并增加重组/分解功能。分解应用模式能够接受用户请求,把其分解成多个请求,
并路由到多个后端应用程序。而将若干对用户的响应重组为一个响应。这样就把某些商
业 逻 辑 迁 移 到 分 解 层 , 但 主 要商业 逻辑 仍 然驻留 在后 端 应用层 。本 书 使用 IBM
WebSphere 应 用 程 序 服 务 器 、 IBM MQSeries 集 成 器 以 及 IBM MQSI 聚 合 插 件
(Aggregator Plug-In),来举例说明分解和重组的功能。此实例中还将阐释 WebSphere
企业服务部所提供的 JMS 接收器。
本书是对红皮书《采用 WebSphere 高级服务器和 MQSI 的用户对商家模式》编号:SG24
-6160 的更新。
本书编写小组
本红皮书由来自世界各地的常驻国际技术支持组织 Raleigh Center 的专家小组编写而成。
Carla Sadtler 国际技术支持组织 Raleigh Center 的高级软件工程师。她在适用于电子商
务领域的 WebSphere 和模式方面创作丰富。早在 1985 年加入 ITSO 之前,就在 Raleigh
分部工作,并担任程序支持代表。她拥有位于格林斯博罗的南卡罗来纳大学数学学位。
xiii
Barry Procopio IBM 全球服务部的 IT 专家。他是 Sun Java 2 认证程序员,在面向
对象语言的开发方面拥有 5 年工作经验,其专业技术领域包括设计模式、面向对象的
分析及设计 (OOAD), 以及 Java 2 企业版 (J2EE) 应用程序开发。
Robert Rehms IBM 全球服务部企业应用程序集成 (EAI) 全国实践方面的 IT 体系
结构设计师。他在分布式计算环境方面拥有超过 12 年的应用程序设计和开发等工作经
验。在 IBM 工作了近两年。其专业技术领域包括在 EAI 环境中应用 MQSeries、
MQSeries 集成器和应用程序设计技术。
衷心感谢以下人士对本项目所做的贡献:
国际技术支持组织 Raleigh Center 的 Geert Van de Putte 和 Bill Moore
特别说明
本出版物旨在帮助 IT 体系结构设计师及 IT 专家设计与应用电子商务应用程序,而无
意为 IBM MQSeries、IBM MQSeries 集成器,以及 IBM WebSphere 应用程序服务器提
供任何编程接口规范。请参阅有关这些产品的“IBM 编程公告”中的“出版物”一节,
了解何种出版物可视为产品文档的详细信息。
xiv
IBM商标
以下术语是 International Business Machines Corporation 在美国和/或其它国家(或地
区)的商标:
e(logo)®
Redbooks Logo
AIX®
S/390®
CICS®
SecureWay®
DB2®
SP™
Encina®
SP2®
Everyplace™
SupportPac™
IBM®
Tivoli®
IBM.COM™
TXSeries™
IMS™
VisualAge®
MQSeries®
WebSphere®
OS/2®
Z/OS™
OS/390®
zSeries™
OS/400®
Lotus®
Redbooks™
Domino™
欢迎评论
您的评论对我们非常重要!
我们希望 IBM 红皮书能为您提供尽可能的帮助。请将您关于本书或其他红皮书的意见
通过以下途径寄给我们:
使用位于以下地址网上联系我们评论红皮书表格:
ibm.com /redbooks
以因特网附注的形式把您的评论发送至:
[email protected]
将您的评论邮寄至第 ii 页载明的地址。
xv
xvi
1
适用于电子商务的模式
IT 体系结构设计师的工作是评估商业问题并设计解决方案。设计师首先收集有关问题、
希望的解决方案,以及需要考虑的所有特殊注意事项或要求的输入。设计师接受此输入
并设计出一套包含一个或多个应用程序的解决方案,而且这些应用程序可提供必需的功
能。
对于我们来说,吸收这些 IT 设计师的经验非常有利,因为我们可以简化未来约定。吸
收此经验并精心设计存储库,可帮助设计师利用此经验按照业经证明的方案创建未来的
解决方案,从而节约时间和资金,并有助于确保产生一套经得起时间考验的、稳定的解
决方案。
IBM 电子商务的工作模式就是如此,其目的是收集业经测试及证明的电子商务方法。收
集到的信息应适应绝大多数情形。通过汇总这些方法并将其分成若干有用类别,我们为
电子商务规划人员、设计人员以及开发人员节约了时间和资金。
将这些方法进一步提炼成有用的具体原则。模式及其关联原则使设计师能以问题和理念
开始,寻求一种适应此理念的概念模式,定义应用程序成功所必需的功能组件,然后使
用原则中概述的编码技术实际创建应用程序。最高级模式定义解决方案中所需的可能商
业交互。商业功能一般属于一个或多个已定义的商务模式。
1
1.1 如何使用电子商务模式
电子商务模式以这样一种方式进行构造:每个详细级别以上一个详细级别为基础。处于
最高级别的是描述电子商务解决方案中涉及实体的商务模式。商务模式描述用户、商业
机构或应用程序以及将要访问的数据之间的关系。
商务模式主要有以下四种:
“自助服务”商务模式,旧称“用户到企业”模式。该模式描述用户与商业应用
程序进行交互以查看或更新数据的情形。
“协作”商务模式,旧称“用户到用户”模式。该模式描述用户之间的交互。这
包括电子邮件和工作流程。
“信息聚集”商务模式,旧称“用户到数据”模式。该模式描述用户访问和处理
从多个来源收集的大量数据的情形。
“扩展企业”商务模式,旧称“企业到企业”模式。该模式描述两个不同企业之
间的程序交互。
如果所有问题都恰如其分地套用这四种模式,那会非常方便,但现实情况通常要复杂得
多。这些模式假定多数问题在细分为最基本成分之后,能够适应其中一种模式。当一个
问题需要多种商务模式时,电子商务模式以“集成”模式的形式提供补充模式。
集成模式使我们可以将多种商务模式捆绑起来解决一个商务问题。集成模式包括:
“访问集成”模式。该模式通过通用入口对多种服务和信息进行前端集成。它负
责处理多种客户机设备类型、单点登录、个性化,以及为应用程序提供一种通用
的外观和感受。
2
“应用程序集成”模式。该模式对多个应用程序和数据进行无缝的后端集成,不
需要用户直接访问。
市场上已认同“商务”和“集成”模式的同一组合,我们把这种组合称为“混合”模式。
目前已有多种“商务”模式和“集成”模式的常见使用情况被确定为“混合”模式:
账户访问
电子商务
门户
购买端中枢
销售端中枢
贸易交换
这些模式构成的不同之处在于,每个类型都有其基本模式,但可容易地扩展“混合”模
式以满足附加的条件。
我们可以查看这些模式(如图 1-1 所示)。此图可以表示某种特定安装中的定制设计,
也可以表示常见的“混合”模式。稍后会详细讨论这些模式。
图 1-1 商务和集成模式
3
一旦确定“商务”模式,接下来的步骤就是定义构成解决方案的高级逻辑组件以及这些
组件之间如何进行交互。这称为“应用”模式。一个“商务”模式通常会包含多个可能
的“应用”模式。应用模式可能拥有用于描述与用户、应用层以及后端应用层进行交互
的表示层的逻辑组件。
应用模式由一个或多个“运行时”模式支持。运行时模式定义代表必须执行的中间件功
能的功能节点。应用模式作为高级应用程序功能的抽象表示而存在,而运行时模式则是
所需中间件功能、要使用的网络结构以及系统管理特性(如负载平衡和安全性)的较为
具体的表示。
一旦确定运行时模式,接下来的逻辑步骤就是确定用于每个节点的实际产品和平台。电
子商务模式有与运行时模式相关的运行时产品映射,描述已用于创建针对此情形的电子
商务解决方案的实际产品。
最后,原则有助于您使用通过经验确定的最佳做法创建应用程序。
4
图 1-2 电子商务组件模式
下面将更为详细地考察各个步骤。
1.1.1
选择商务模式
当面临针对某个商务问题设计解决方案的挑战时,第一步是以高级视角审视将要实现的
目标。应描述建议的商务情形,并应使每个元素与适当的商务模式匹配。您可能会发现
整个解决方案会需要一种或多种商务模式。
例如,假定某个保险公司想要减少投入处理客户咨询的呼叫中心的时间和资金。通过允
许客户在网上查看其保险单信息和请求更改,就能够极大地节约通过电话处理这些事务
所需的资源。目的是使保险单持有者可以查看其存储在系统数据库中的保险单信息。
“自助服务”商务模式非常适合于这种情形。自助服务商务模式可用于用户需要直接访
问商务应用程序和数据的情形。
5
1.1.2
选择应用模式
“应用”模式把应用程序分为若干最基本的概念组件,确定应用程序的目标。在我们的
实例中,应用程序属于自助商务模式,目标是创建一个使用户可以访问后端信息的简单
应用程序。下面的应用模式可满足此要求:
图 1-3“自助服务”应用模式
应用模式包含一个处理用户请求以及对用户响应的表示层。应用层代表处理对后端应用
程序和数据访问的组件。右边的多个应用程序框表示包含商业数据的后端应用程序。通
信类型指定为同步(一个请求/一个响应,然后是下一个请求/响应)或异步(多个混
合的请求和响应)。
假定情形更复杂一些。例如,汽车策略和房屋拥有者策略分别保存在两个独立且不同的
数据库中。用户请求实际上会需要来自多个不同后端系统的数据。在这种情况下,就需
要把该请求分为多个请求(分解请求),并发送到两个不同的后端数据库,然后收集从
请求返回的信息,并把信息转换为响应的形式(重组)。在这个案例中,以下应用模式
较为适当。
6
图 1-4 自助服务应用模式 6
此应用模式通过添加分解和重组功能,扩展了访问后端数据的应用程序节点的概念。
1.1.3
选择“运行时”模式
通过执行更明确的功能,可以进一步细化“应用程序”模式。每个功能都与一个运行时
节点相关联。实际上,这些功能或节点既可以存在于单独的物理设备,也可以共存于同
一台设备。在“运行时”模式中,这是不相关的。焦点在于所需的逻辑节点及其在整个
网络结构中的布局。
例如,假定我们的客户已确定其解决方案适合于“自助服务”商务模式,而且应用模式
1 可最恰当地描述相关情形。接下来的步骤就是确定最适合于其情形的运行时模式。
客户知道将会有因特网用户访问其商业数据,并因此而需要一种安全测度。可以在各种
应用层实施安全机制,但防御的第一线几乎始终是定义谁和什么可以跨越物理网络界限
进入其公司网络的一个或多个防火墙。
该客户还需要确定实施应用程序和安全测度所需的功能节点。下面的运行时模式是其选
项之一。
7
图 1-5“自助”运行时模式
通过在运行时模式上重叠应用模式,您可以看到每个功能节点在应用程序中履行的角
色。将通过 Web 应用程序服务器实施表示层和应用层,该服务器兼有 HTTP 服务器和
应用程序服务器的功能。它处理静态和动态网页。
应用程序安全性是由 Web 应用程序服务器利用通用的中央目录和安全服务节点处理的。
使运行时模式不同于其它模式的特征是两道防火墙之间的 Web 应用程序服务器的位置。
下面的运行时模式就是这种情况的一个变体。它通过把 HTTP 服务器功能从应用程序服
务器分开,把 Web 应用程序服务器分成两个功能节点。HTTP 服务器(Web 服务器重定
向器)将服务于静态网页,并把其它请求重新定向至应用程序服务器。它把应用程序服
务器功能迁移到第二个防火墙之后,因而进一步增强了安全性。
8
图 1-6“自助”运行时模式变体
上面只是可能的可用运行时模式中的两个实例。每个应用模式定义有一个或多个运行时
模式。可以通过修改这些运行时模式来适应客户的需要。例如,客户可能希望添加负载
平衡功能。
1.1.4
选择运行时产品映射
定义应用程序网络结构中最后的一步是实际产品与一个或多个运行时节点相关。电子商
务网站模式将通过已按相应能力测试的产品显示各种运行时模式。产品映射面向某种特
定平台,尽管更可能的情况是,客户的网络中有多种平台。在这种情况下,只是一个混
合与搭配的问题。
例如,可以使用图 1-7 描述的产品集实施上面的运行时变体。
9
图 1-7 运行时产品映射
1.1.5
应用原则
应用模式、运行时模式以及运行时产品映射的目的都是为了指导您定义应用程序要求以
及网络布局。尚未涉及到实际的应用程序开发。模式网站为各种应用模式提供若干原则,
其中包括开发、实施和管理应用程序的技术。
设计原则向您传授设计应用程序的技巧和技术。
开发原则指导您完成从要求阶段一直到测试和转出阶段的应用程序创建过程。
系统管理原则解决日常操作问题,包括安全性、备份和恢复、应用程序管理等。
性能原则提供如何提高应用程序和系统性能的信息。
10
第一部分
自助服务模式
11
12
2
自助服务商务模式
传统上,企业投入大量资源为客户、供应商和员工提供信息。这些资源采取呼叫中心、
邮寄等形式。企业还以客户概况的形式维护有关客户的信息。对这些客户概况的更新通
常是通过电话或邮件进行处理的。
通过用户界面,自助服务概念使此信息对客户来说唾手可得,无论该用户界面是网站、
个人数字助理还是某种其他客户机界面。电子商务应用程序以一种易于访问的方式使适
当用户获得其所需信息,从而减少人机交互,提高用户满意度。
13
2.1 自助服务应用程序
为客户提供自助服务的应用程序的关键元素包括:明确的导航方向、扩展的搜索功能以
及有用的链接。其广受欢迎的方面是具有可以直接指向网上代表的链接,这些网上代表
可以回答问题,并在需要时提供人机界面。
下面列出一些自助服务应用程序的实例:
保险公司可以让用户方便地访问保险信息,并允许他们在网上申请保险。
抵押贷款公司可以在网上发布有关其贷款政策和贷款利率的信息。客户可以查看
其当前抵押信息、更改其付款选项或申请抵押贷款。
科研机构通过把学术论文放在网上,从而使有兴趣的用户能够查阅。
银行允许客户在网上访问其账户并支付账款。
知名技术撰稿人团体把其作品放在网上,该团体把即将启动的项目在网上列出并
让可能的参加者在网上申请,从而招募技术参加者。
公司允许其员工在网上查看当前的人力资源政策。员工可以在网上更改其医保计
划、扣税信息、股票购买计划等,而无需给人力资源办公室打电话。
2.2 自助服务应用模式
如您所见,自助服务商务模式包含多种用途。此模式的应用从允许用户查看出于某一目
的而明确创建的数据这样非常简单的功能,到接受用户的请求、将其分解成要发送到多
个不同数据源的多个请求、个性化信息以及重组成一个对用户的响应不等。为此,目前
有七个定义的应用模式适应此功能范围。接下来将概述这些应用模式。有关更多详细信
息,请参阅《电子商务模式:复用策略》(Jonathan Adams、Srinivas Koushik、Guru
Vasudeva、George Galambos 著,IBM 出版社出版,编号:1-931182-02-7)。
14
图 2-1 自助服务应用模式
1.
独立的单个通道(Stand-alone single channel)应用模式:提供不需要与现有应
用程序或数据集成的独立应用程序。该模式假定有一个传送通道,非常像 Web 客
户机,尽管该模式也可能是其它什么事情。它由处理用户界面各个方面的表示层
以及包含访问本地数据库数据的商业逻辑的应用层构成。两层之间的通信是同步
的。表示层把请求从用户传递到 Web 应用层中的商业逻辑。处理该请求,并向表
示层发回响应,以便传递给用户。
2.
直接集成的单通道应用模式:提供用户和现有后端应用程序之间的点到点连接。和
独立的单个通道应用模式一样,直接集成的单通道应用模式也假定有一个传送通
道,表示层处理用户界面。商业逻辑可以驻留在 Web 应用层和后端应用程序中。
Web 应用层可以访问主要作为此应用程序结果存在的本地数据,例如,客户轮廓
信息或缓存数据。它还负责访问一个或多个后端应用程序。后端应用程序包含商
业逻辑,并负责访问现有后端数据。表示层和 Web 应用层之间的通信是同步的。
Web 应用层与后端之间的通信可以是同步的,也可以是异步的,具体取决于后者
的特征和功能。
15
3.
原样主机应用模式(As-is Host application pattern):提供对现有主机应用程序
的简单直接访问。应用程序没有更改,但把用户访问从绿屏类型(green-screen
type)访问转换为基于 Web 浏览器的访问。这实施起来非常迅速,但对用户看到
的应用程序外观不做任何更改。商业逻辑和表示都由后端主机处理。由于界面仍
然是主机驱动的,此模式更适合于员工熟悉应用程序的企业内部互联网解决方案。
4.
定制表示到主机应用模式:这是一种比原样主机模式更进一步的应用模式。后端主
机应用程序仍然保持不变,但 Web 应用程序现在将表示从后端主机应用程序转换
为用户容易使用的图形视图。后端主机应用程序不会觉察此转换。
5.
路由器应用模式:路由器应用模式使用集中星型(a hub-and-spoke)体系结构提
供从多个通道到多个后端应用的智能路由。用户和后端应用之间的交互是一对一
关系,意思是用户一次一个地与应用进行交互。路由器维持与后端应用的连接,
并在适当时复合连接(pools connections),但不存在应用程序之间的真正集成。
路由器可以使用只读数据库,很可能是查找路由信息。主要商业逻辑仍然驻留在
后端应用层。
此模式假定用户从多种客户机类型(Web 浏览器、VRU 或信息站)访问应用程序。
路由器应用模式提供一种用于访问多个后端应用程序的公用界面,并作为这些后端
应用程序与传送通道之间媒介。为此,路由器应用模式可以使用集成模式的元素。
6.
分解应用模式:分解应用模式是对路由器应用模式的展开,具有路由器应用模式的
所有特性和功能,并增加了重组/分解功能。分解应用模式能够接受用户请求,
并将其分解为多个请求,以便路由到多个后端应用程序。响应重组为对用户的一
个响应。这样就把某些商业逻辑迁移到分解层,但主要商业逻辑仍然保留在后端
应用层。
16
7.
代理应用模式:代理模式包括分解层功能,而且在应用程序中把个性化包含在内,
从而提供一种以客户为中心的视图。代理层从监控其习惯或从 CRM 中存储的信息
收集有关用户信息。代理层使用此信息定制向用户显示的视图,并可通过推送提
供给用户执行交叉销售功能。
2.3 本书中使用的应用模式
本书其余部分将集中讨论基于路由器和分解应用模式的电子商务解决方案。
17
18
3
运行时模式
路由器应用模式代表以下任务的起点:提供把信息从后端系统传送到多个交付通道的复
杂电子商务应用程序,与此同时,仍然可以利用历史应用程序的数据完整性和性能。
分解应用模式通过把来自客户机的一个复合请求分解成几个简单请求,并把它们智能地
路由到多个后端应用程序,从而扩展了路由器应用模式。
下一步是选择最匹配应用程序要求的运行时模式。运行时模式使用节点组合功能和操作
组件。节点之间相互连接,共同解决商业问题。每个应用模式均导致一个或多个基础运
行时模式。
19
3.1 节点类型简介
运行时拓扑由几个代表特定功能的节点构成。多数拓扑由一组核心的公用节点构成,再
加上一个或多个该拓扑所特有的节点。为了理解运行时拓扑,您需要查阅以下的节点定
义。
集成服务器
集成服务器可以托管(hosts)应用程序逻辑,该应用程序逻辑可访问和使用现有数据库
的信息、交易监控系统的交易功能以及应用程序包的应用程序功能等。集成服务器可以
逐个访问后端应用程序,也可以用新的方式组合此信息和功能。
集成服务器至少可以充当多个表示层(例如,呼叫中心、分支机构、Web 浏览器)集成
点的角色,这样它们就可以共享第 2 层和第 3 层的基础结构和应用程序。
Web 应用程序服务器
Web 应用程序服务器节点是包含 HTTP 服务器(也称为 Web 服务器)的应用程序服务
器,并且一般供 HTTP 客户机访问而精心设计,并能托管表示逻辑和商业逻辑。
Web 应用程序服务器节点是信息性(基于发布的)Web 服务器的功能扩展。它提供技术
平台,并且包含支持利用 Web 浏览器技术的用户访问公共信息和用户特有信息的组件。
对于后者,节点提供允许用户与共享应用程序和数据库进行通信的强健的服务器。这样,
节点就可作为连接商业功能(如银行、借贷和 HR 系统)的接口。
此节点将以公司对公司为前提提供,或者出于安全理由,宿主在一个非军事区(DMZ)
内部的企业网络中。多数情况下,可以使用诸如 SSL、IPSec 这样的服务安全地访问此
服务器。
在最简单的设计中,此节点可管理超媒体文档和不同应用程序功能。对于更为复杂的应
用程序或要求更可靠安全机制的应用程序,建议在内部网中的单独 Web 应用程序服务
器节点上部署应用程序。
节点上可能包含的数据有:
要下载到客户机浏览器的 HTML 文本页、图像、多媒体内容
20
JavaServer Pages(JSP)文件
服务件(Servlets)、Enterprise Beans
应用程序库,例如,动态下载到客户机工作站的 Java 小程序
公钥基础设施(PKI)
PKI 是一套基于标准的技术和商业服务,它们支持两个独立实体(例如,公共用户与公
司)间通过因特网的安全交互。就本红皮书中定义的拓扑而言,PKI 支持使用 SSL 协议
对服务器到浏览器客户机的身份验证。
域名系统(DNS)节点
DNS 节点有助于确定与请求信息符号地址(URL)关联的物理网络地址。尽管也是在访
问站点上实施的,但 DNS 是因特网服务供应商的地址。
用户节点
此节点通常是支持某种商用浏览器(如 Netscape Navigator 或 Internet Explorer)的个人
计算设备(PC 等)。浏览器的级别应支持 SSL 和某种级别的 DHTML。设计人员还应
考虑此节点可以是普遍计算设备,如个人数字助理(PDA)。
目录和安全服务节点
此节点提供有关此 Web 应用程序系统识别的资源和用户的位置、功能和各种属性(包
括用户 ID/密码对(password pairs)和证书)的信息。此节点可以提供各种安全服务(身
份验证和授权)信息,也可以执行实际安全处理,例如,验证证书。最新设计中的身份
验证功能验证对 Web 服务器的 Web 应用程序服务器部分的访问,但也可以验证对数据
库服务器的访问。
协议防火墙和域防火墙节点
防火墙提供可用于控制从不太可信网络到较为可信网络的访问的服务。传统的防火墙服
务实施包括:
屏蔽路由器(此设计中的协议防火墙)
应用程序网关(域防火墙)
21
两个防火墙节点以增加计算资源要求为代价提供渐增的保护级别。协议防火墙通常是作
为 IP 路由器实施的,而域防火墙则是专用的服务器节点。
Web 服务器重定向器节点
为了把 Web 服务器从应用程序服务器分离开来,引入一种所谓的“Web 服务器重定向
器节点”(称为“重定向器”)。Web 服务器重定向器与 Web 服务器配合使用。Web
服务器服务于 HTTP 页,而重定向器则把服务件和 JSP 请求转发到应用程序服务器。使
用重定向器的优点是,您可以把域防火墙后面的应用程序服务器迁移到安全网络中,从
而得到比在非军事区(DMZ)内更好的保护。此节点可从 DMZ 提供静态页。
可以通过反向代理服务器或通过 Web 服务器插件(如 IBM WebSphere Application Server
高级版的远程 OSE 功能)实施重定向器。
现有应用程序和数据节点
现有应用程序是在内部网中安装的节点上运行和维护的。这些应用程序提供使用内部网
中维护数据的商业逻辑。这些现有应用程序和数据节点的数目和拓扑取决于历史系统使
用的特定配置。
3.2 路由器模式的基本运行时模式
在路由器应用模式中,路由器层用作表示层中传送通道的集成点,这样就可以访问各个
后端应用程序。在基本运行时模式中,路由器层的功能由集成服务器执行。表示层的功
能由应用程序服务器执行。
22
图 3-1 用于路由器应用程序模式的基本运行时模式
此模式使用 Web 服务器重定向器节点为客户机提供静态 HTML 页。对动态数据的请求
被转发到应用程序服务器。因此表示逻辑跨越两个节点。这两个节点共同提供能够处理
多个完全不同表示样式的表示层。使用重定向器使您可以把大部分商业逻辑置于协议和
域防火墙的保护之下。
除表示逻辑(如 JSP)之外,应用程序服务器还包含一些商业逻辑。其主要形式是访问
后端应用程序的控制服务件。应用程序服务器根据用户输入建立请求,并将其传递到集
成服务器。主要商业逻辑驻留在后端应用程序中。
集成服务器检查该请求、确定适当目的地,以及将其转发到选定的后端应用程序。这可
能会涉及诸如消息转换、协议转换、安全管理以及会话集中的活动。集成服务器可以把
数据库用作高速缓存设备来查看路由信息,或用来保存中间数据。
23
对应用程序服务器资源的访问受应用程序服务器安全功能的保护,而对于集成服务器资
源的访问则受集成服务器安全功能的保护。两种服务器进行身份验证和授权所需的用户
信息,都存储在内部网域防火墙之后的目录和安全服务之中。
3.3 用于分解的基本运行时模式
表面上,用于分解应用程序模式的运行时模式看起来与用于路由器应用模式的运行时模
式相同。运行时模式描述方面唯一可见的区别在于应用模式重叠。但是,图形中这一微
小的细节却代表着集成服务器提供的功能方面的巨大变化。
图 3-2 用于分解应用程序模式的基本运行时模式
集成服务器仍然检查消息,并将消息路由到适当后端应用程序。但接下来,集成服务器
还会向前更进一步:获取一条复杂消息、将其分解成多条消息,然后将这些消息路由到
适当后端应用程序。集成服务器还能够管理这些消息,这样,就可以等待响应,并把响
应重组为一个返回用户的单个响应。这会有效地利用多个完全不同的后端应用程序,并
为用户将它们统一为一个接口。
24
集成服务器可以将本地数据库用于前面描述的路由功能,并将其作为存储消息分解和重
组所需信息的正在开发中的数据库。
25
26
4
运行时产品映射
选择运行时模式之后,接下来就是确定要使用的实际产品和平台。建议您根据以下几个
考虑事项做出最终的平台推荐:
现有系统和平台投资
可以利用的客户和开发商技能
客户选择
选择平台应适应客户环境,并确保服务质量,如可扩展性和可靠性,这样,解决方案才
可以随电子商务的发展而扩展。
在本红皮书中,通过 IBM 的四种主要产品来完成自助服务路由器应用模式和分解应用
模 式 的 实 施 工 作 。 执 行 应 用 程 序 的 路 由 器 和 分 解 功 能 的 集 成 服 务 器 是 使 用 IBM
MQSeries Integrator 实施的。应用程序服务器是使用 WebSphere Application Server 版本
4.0 高级版实施的。主要的后端应用程序是使用 WebSphere Application Server 版本 4.0
高级版和 WebSphere Enterprise Services 实施的。最后,各种服务器之间的传输是使用
IBM MQSeries 实现的。
本章将介绍应用程序使用的这些主要产品,并概述适用于运行时模式的产品。
27
4.1 IBM MQSeries产品家族
路由器和分解应用模式要求对企业后端应用程序进行网络革命(Web-enablement)。
后端集成需要一种使应用程序能够相互对话的机制,通过这种机制,就可以在支持 Web
的前端与企业后端应用程序间请求信息和处理事务。在这种情况下,通常会需要对这些
消息执行中间操作,例如,确定处理请求的适当后端系统,或把数据从一种格式转换为
另一种格式。
在满足此要求的情况下,MQSeries 产品家族就可作为高度可扩展的、面向消息的中间
件(MOM)基础结构,以便进行应用程序消息发送、代理、路由、聚集以及商业数据
转换。MQSeries 产品家族还提供应用商业规则所需的工具,该商业规则按照满足要求
的数据行事,并提供在多种平台之间扩展和重复利用企业应用程序的灵活性。
本书重点讲述两种 IBM MQSeries 产品:MQSeries 和 MQSeries Integrator:
IBM MQSeries 5.2
MQSeries 家族的中心是 MQSeries 本身,它具有 MOM 传输层所需的功能。它使用
多种通信协议在 35 种以上的行业平台之间提供可靠的一次性消息发送服务。
通过网络的消息数据传送是利用 MQSeries 队列管理器网络实现的。各个队列管理
器主持存储消息的容器的本地队列。通过远程队列定义和消息通道,可以把数据传
送到其目标队列管理器。
要使用 MQSeries 传输层的服务,应用程序必须与某个 MQSeries 队列管理器建立连
接,该队列管理器的服务使该应用程序能够接收(获得)来自本地队列的消息,或
把消息发送(置于)任何队列管理器的任何队列。可以直接建立该应用程序的连接
(其中,队列管理器在应用程序的本地运行),也可以作为某个队列管理器(可通
过网络获得)的客户机建立连接。
28
动态工作负载分配是 MQSeries 的另一项重要功能。此功能使属于同一群集的一组
队列管理器分担工作负载。这使 MQSeries 能在可用资源间自动平衡工作负载,并
在某个系统组件发生故障时提供热备份功能。对于那些需要维持全天可用性的公司
来说,这一功能至关重要。
MQSeries 支持多种应用程序编程接口(MQI、AMI、JMS),这些接口为多种编程
语言以及点到点和发布/预订通信模式提供支持。除了支持应用程序编程之外,
MQSeries 还为多种其它产品提供许多连接器和网关,这些产品包括 Microsoft
Exchange、Lotus Domino、SAP/R3 CICS、IMS,等等。
IBM MQSeries Integrator 版本 2.02
MQSeries Integrator(MQSI)通过增加企业商业规则驱动的若干信息请求的消息代
理、路由和聚集功能,扩展了 MQSeries 的消息发送功能。它还提供转换消息的附
加智能、过滤消息(基于主题或基于内容)的可能性以及用于丰富、缓存或仓储消
息的数据库功能。为了增加更大灵活性和功能性,MQSI 提供一个框架,该框架通
过插件把功能扩展到满足特定要求的用户编写的解决方案或第三方解决方案。
随着您的应用程序的成熟,可能有必要了解一下 MQSeries 家族的其它产品成员:
IBM MQSeries Everyplace
MQSeries Everyplace(MQe)可提供一种用于轻型设备间传递消息的机制,这些设
备包括电话、个人数字助理(PDA)、传感器、膝上型电脑等。MQe 还提供强大
的移动通信支持,并支持通信网络脆弱的地方产生的要求。MQe 通过提供一次性
可靠传递秉承了 MQSeries 的服务质量,同时还能在 IBM MQSeries 支持 35 种平台
的任意平台上交换消息,以及与 WebSphere 家族成员交换消息。许多 MQe 应用程
序需要在不受因特网防火墙保护的情况下运行。为此,MQe 还提供复杂的安全服
务。
IBM MQSeries Workflow
MQSeries Workflow 是 IBM 产品家族的成员,用于商业集成。它同样基于基本
MQSeries 提供的消息发送技术,但它给应用程序集成添加了进一步的维度。
MQSeries Workflow 不仅关于应用程序,而且关于集成商业流程中的所有资源。它
可确保恰当信息在恰当时间到达商业流程中的恰当目标,无论这个目标是人还是应
用程序。例如,凭借关键的 MQSI 消息流,您能够跟踪错误,并把这些错误定向到
29
某个队列。此消息到达此队列中,会触发 MQSeries Workflow 过程,这样,就会有
适当人员去查明并解决问题。
有关详细信息,可在 MQSeries 网站上查阅,而且多个 SupportPacs 提供一系列增强或扩
展这些产品的有用工具。
可按以下地址访问 MQSeries SupportPacs:
http://www.ibm.com/software/ts/MQSeries/txppacs .
4.2 IBM WebSphere Application Server
IBM WebSphere Application Server 版本 4.0 在电子商务环境中提供 Web 服务器和应用程
序服务器服务。它根据集成的 WebSphere 软件平台产品或其他第三方产品,支持定制构
建应用程序。这样的应用程序从动态 Web 表示到复杂的事务处理系统不等。
WebSphere Application Server 版本 4.0 在支持行业开放性标准方面处于领先地位。
WebSphere Application Server 版本 4.0 通过实施一套种类齐全的企业 Java 开放性标准,
提供完全的 Java2 平台企业版(J2EE)符合性。它还提供对关键 Web 服务开放性标准的
内置支持,因而是应用企业 Web 服务解决方案的理想产品。
WebSphere Application Server 版本 4.0 的推出代表着向所有主流平台支持的单一代码基
迈进了一步。此版本灵活且可扩展的配置使您可以对不断变化的市场做出响应,而且无
需迁移到不同的技术基础。
4.2.1
WebSphere Application Server版本4.0高级版
WebSphere Application Server 版本 4.0 高级版有三种不同的可用配置:
完全高级版配置(AE)通过与数据库、面向消息的中间件以及历史系统和应用程
序进行集成,凭借群集支持,提供应用程序服务器功能。此配置适用于需要构建
具有极高事务处理能力、易管理性、可用性和可扩展性应用程序的企业,这些应
用程序需要具备分布式安全功能和远程管理功能。
30
高级版单一服务器配置(AEs)在单个运行时过程内提供应用程序服务器功能。此
配置适用于需要构建独立或部门级应用程序的企业,这些应用程序是面向事务或
消息的,而且无需故障旁路、工作负载管理或远程管理。
高级版开发人员许可证(AEd)向需要易用环境构建和测试电子商务应用程序的开
发人员提供应用程序服务器功能。它适用于正在寻找友好且功能强大的单元测试
环境(尤其是可与 IBM 工具无缝集成的环境)的开发人员。
有关 WebSphere Application Server 版本 4.0 高级版的详细信息,请访问:http://
www.ibm.com/software/webservers/appserv/advanced.html
4.2.2
IBM WebSphere Application Server版本4.0企业版
IBM WebSphere Application Server 版本 4.0 企业版(EE)扩展了 WebSphere Application
Server 版本 4.0 高级版。它包括 WebSphere Enterprise Services,其可以加快应用程序开
发速度、使应用程序更好地响应商业变化、适用于 J2EE 内的现有 IT 资产以及支持更高
的服务质量。企业版还包括 IBM TXSeries 技术,以适应快速发展、高度分布的电子商
务基础结构最复杂的需要,而且这样的电子商务基础结构需要极高水平的吞吐量。最后,
企业版软件包中包含 MQSeries,以便容易地连接多个不同平台之间的应用程序和系统。
适用于 IBM z/OS 和 IBM OS/390 的 WebSphere Application Server 版本 4.0 完全利用
IBM zSeries 和 IBM S/390 体系结构实现卓越的可扩展性、性能、安全性和可用性。在
z/OS 平台上运行 WebSphere Application Server 版本 4.0 可提供与具有最早服务质量的
分布式平台上提供的相同企业服务。适用于 z/OS 和 OS/390 的 WebSphere Application
Server 版本 4.0 利用支持 LPAR 群集技术的工作负载管理器,并且完全支持 IBM Parallel
Sysplex 技术。
有关 IBM WebSphere Application Server 版本 4.0 企业版的详细信息,请访问:
http://www.ibm.com/software/webservers/appserv/enterprise.html
31
4.2.3
WebSphere Enterprise Services
Enterprise Services 包含在 IBM WebSphere Application Server 版本 4.0 企业版中,目前包
括以下组件:
商业规则 Beans。此框架把 WebSphere Application Server 企业版的范围扩大为可支
持其商业规则具体化的商务应用程序。
扩展的消息发送支持。WebSphere 企业应用程序可以使用扩展的消息发送服务来自
动接收来自输入队列(JMS 目的地)的消息,并协调对这些消息的处理。这可实
现把消息以自动异步传送方式传递到企业应用程序,而不需要该应用程序排队明
确轮询消息。
国际化服务。此支持为针对不同地区、代码组或时区配置的不同设备上运行的应
用程序进程提供解决方案。
WorkArea 服务。此支持赋予应用程序设计人员一种为某个应用程序创建工作区、
在其中插入信息以及进行远程调用的方法。
CORBA 支持。此支持能够使用提供服务的服务器对象以及使用此服务的客户机之
间的 CORBA 接口。
ActiveX 客户机到 EJB 桥。此支持使诸如 Visual Basic、VBScript 和 Active Server
Pages 这样的 ActiveX 程序可以访问 Enterprise JavaBeans。
本书中使用的应用程序将使用 Enterprise Services 提供的扩展消息发送支持。
4.3 运行时产品映射
图 4-1 显示一个基于 Windows 2000 操作系统平台的运行时产品映射。我们使用此运行
时节点和产品的组合实施此项目中创建的路由和分解应用程序。
32
图 4-1Windows 2000 产品映射
IBM MQSeries Integrator 用于提供路由器和分解功能。它具有从 MQSeries 队列检索消息
并根据类型分析该消息,以及进行与该消息相关的逻辑决策的能力。例如,MQSI 代理
程序可以更改消息、将其转换为不同的格式、从其创建多个新消息,并把信息存储到数
据库或执行多个可能会需要的其他操作。根据消息内容或其它来源(如本地数据库)输
入,MQSI 可以接收消息,并将其路由到适当后端应用程序,以处理请求。
为使之更进一步,通过添加 MQSI 聚集器插件(Aggregator Plug-In),MQSI 可以获取
输入消息,并将其拆分(分解)成多个后端请求。然后聚集器就可以等待对这些消息的
答复,并把它们重组为一个要返回到用户的响应。
MQSeries 为这些消息提供传送机制。在实施中,我们在每个服务器上使用一个 MQSeries
队列管理器来传送消息。Java 应用程序使用 JMS 把消息放在本地队列上。然后,MQSeries
负责把此消息发送到适当目的地,在我们的案例中,应该是 MQSI 代理程序。
33
通过使用 Web 服务器重定向器节点,我们可以把多数商业逻辑置于内部网络,即将其
置于两道防火墙之后。重定向器是使用 WebSphere Application Server HTTP 传送插件实
施的。重定向器提供静态 HTML 页,并将对动态内容的请求转发到使用 HTTP 协议的
WebSphere 应用程序服务器。
我们应用程序的后端系统由 WebSphere Application Server 和历史的 CICS 应用程序构成。
WebSphere Application Server 使用 WebSphere Enterprise Services 提供的扩展消息发送支
持。此支持能够实现接收未经请求的 MQSeries 消息,并执行会话 Bean 以执行与该消息
关联的商业逻辑。
图 4-2 显示与主要平台相同的使用 AIX 的实施方法。
图 4-2AIX 产品映射
34
第二部分
原则
35
36
5
技术选项
本章考察在基于开放性标准和 Java 编程(IBM 电子商务软件战略已经概述)的 Web 应
用程序中您应考虑采用的技术。以平台独立性为主要焦点,相关建议都以重复利用、灵
活性和互操作性要求为指导,并且以 Java2 平台企业版(J2EE)概述的开放性行业标准
为基础。随着 J2EE 规范日趋成熟,并包含更为广泛的企业体系结构视图,选择余地将
会越来越大。这些建议已根据 J2EE1.2 规范得以更新。
我们将考察同时适用于应用程序客户机和服务器端的技术。有些技术(如 Java 和 XML)
可适用于两者。同样,设计中使用的客户端技术选择会针对服务器端要求考虑事项,比
如是否为客户端存储或动态创建元素。另外,我们还将谈及无线领域的一些最新技术选
择。
下面几节将详细介绍在设计中您需要考虑的许多技术。我们建议采用以下技术:
HTML
Java 服务件和 JavaServer Page
XML
连接器
Enterprise Beans
37
JDBC
其他企业 Java API
这些技术用于以下电子商务应用程序的逻辑模型情形。此模型与 GUI 开发中的“模型-
视图-控制器”方法有相似之处,把表示逻辑描绘为由交互控制(Java 服务件实施)和
页面构造(JavaServer Page 实施)构成。可以使用 Beans 和/或 Enterprise Beans 实施商
业逻辑,具体情况取决于应用程序的事务处理特征。商业逻辑可能需要使用适当的连接
器技术访问外部资源。
图 5-1 电子商务应用程序(使用推荐的核心技术)的逻辑结构
我们将讨论以下技术及其使用局限性:
DHTML
JavaScript
Java 小程序
5.1 Web客户机
图 5-1 显示建议 Web 客户机采用的技术。
38
图 5-2Web 客户机技术模型
这些客户机是“瘦客户机”,具有极少或没有应用逻辑。在服务器上管理应用程序,并
下载到请求客户机。应用程序的客户机部分应采用 HTML、动态 HTML(DHTML)、
XML 和 Java 小程序实施。
以下几节将概述一些您应考虑的技术,但请记住,您的选择可能会受到客户或发起者政
策的制约。例如,出于安全原因,某些政府机构只允许在 Web 客户机使用 HTML。
5.1.1
Web浏览器
Web 浏览器是 Web 客户机的基本组件。对于基于 PC 的客户机,浏览器一般包含对
HTML、DHTML、JavaScript 和 Java 的支持。有些浏览器已开始增加对 XML 的支持。
在用户控制下,还有多种其他技术可以配置为“插件”,例如,RealNetworks 的 RealPlayer
或 Macromedia Flash。
39
作为一名应用程序设计人员,您必须考虑采用的技术级别是否可在用户的浏览器中使
用,您也可以给应用程序添加逻辑,以便根据浏览器级别进行少量修改。关于插件,您
需要考虑目标用户群的哪些部分具有该能力。
需要采取交叉浏览器策略来确保进行稳定的应用程序开发。尽管其中许多技术选择已经
成熟,但各种浏览器供应商对其支持情况并不一致。开发人员必须了解应用程序采用的
所有功能的浏览器兼容性。一般情况下,开发人员需要按最低标准进行编码,它至少能
够使用编程技术区分浏览器类型。这里的关键决策是确定由老式浏览器、其它平台(如
Linux 和 Mac)甚至最新浏览器进行处理时的应用程序要求和行为。
在 J2EE 模型中,Web 浏览器起着客户机容器的作用。模型要求该容器提供如 Java2 平
台标准版(J2SE)中定义的 Java 运行时环境。但是,对于一个将由具有不同浏览器功
能的最广大用户组访问的电子商务应用程序来说,客户机通常是用 HTML 编写的,无
需任何其他技术。但有时可以根据用户价值以及所开发项目的机构政策,考虑有限度地
使用其他技术,比如使用 JavaScript 进行简单的编辑检查。
新设备的出现为设计工作带来新的考虑事项,具体是关于设备可以呈现的内容流以及浏
览器更为有限的功能。例如,WAP(无线应用协议)使设备能够呈现用 WML(无线标
记语言)发送的内容。
5.1.2
HTML
HTML 是一种文档标记语言,支持浏览器呈现的超级链接。它包括用于简单窗体控件的
标记。许多电子商务应用程序是使用 HTML 严格汇编的。其优点是,客户端 Web 应用
程序可以是简单的 HTML 浏览器,从而使功能不太强的客户机能够执行电子商务应用
程序。
HTML 规范为具有各种字体、颜色、列表、表格、图像和窗体(文本字段、按钮、复选
标记(checkbooks)以及单选按钮)的文本定义用户界面(UI)元素。这些元素适合于
显示多数应用程序的用户界面。但缺点是它们具有一般的外观和感受,缺乏定制功能。
因此,有些电子商务应用程序开发人员用其它用户界面技术对 HTML 进行补充,以便
增强视觉体验,但需要维持目标用户群的正常访问,同时遵守公司关于 Web 客户机技
术的政策。
40
由于多数 Web 浏览器可显示 HTML3.2 版,所以这是构建应用程序客户端的最低公共标
准。为确保兼容性,开发人员应通过验证器工具,以单位测试页面。同时可以获得免费
工具,如http://validator.w3.org/。
5.1.3
动态HTML
DHTML 在设计和显示用户界面方面具有极高的灵活性。尤其是 DHTML 包括级联样式
表(CSS),CSS 可为显示的各个部分创建不同的字体、页边距和行间距。并可使用绝
对坐标精确定位这些元素。
DHTML 的另一个优点是,通过文档对象模型和事件模型增强 HTML 页的功能性级别。
文档对象使诸如 JavaScript 这样的脚本语言能够控制 HTML 页的各个部分。例如,可以
按照某个脚本的命令,在窗口内移动、隐藏或显示文本和图像。而且,当鼠标移动到某
个链接上时,可以使用脚本更改该链接的颜色或图像,也可以验证某个窗体的文本输入
字段,而无需将该窗体发送到服务器。
遗憾的是,使用 DHTML 也有若干缺点。最大的缺点是,存在两种不同的实施(Netscape
和 Microsoft),而且只能在较新的浏览器版本上找到。两者中小部分基本功能是相同的,
但在许多方面存在差异。最显著的差异是,Microsoft 允许使用 JScript 或 VBScript 修改
HTML 页的内容,而 Netscape 只允许使用 JavaScript 操作(移动、隐藏、显示)内容。
由于浏览器支持级别各不相同,因此必须使用交叉浏览器设计策略,以确保 DHTML 元
素的表示和行为适当。一般情况下,不建议使用此技术,除非需要其功能来满足可用性
要求。
5.1.4
CSS
级联样式表使您可以定义 HTML 文档的常用外观和感受。此规范描述如何以打印和联
机模式显示 Web 文档。
41
CSS 被定义为一组由选择器识别的规则。当客户机浏览器处理选择器时,使选择器与特
定 HTML 标记相匹配,然后根据标记的属性应用选择器。这样,就可以对颜色、字体、
页边距和边框进行全局控制。此外,还有更多高级命令可用于控制像素坐标。可以对相
关样式表命令进行组合,然后将其作为单独的模板文件显示出来,以便大量网页进行引
用。
CSS 被定义为级别 1 和级别 2 规范。级别 1 规范是根据 HTML 编写的,而级别 2 可扩
展为可包含 XML 文档的一般标记样式。使用 CSS 的开发人员通过验证器工具进行单位
测试。这样的验证器工具如:http://jigsaw.w3.org/css-validator/。
由于浏览器支持级别各不相同,因此必须使用交叉浏览器设计策略,以确保 CSS 元素的
表示和行为适当。一般情况下,请慎用此技术,以防破坏规范元素的支持。
5.1.5
XML(客户端)
XML 使您可以利用文档类型定义(DTD)中指定的标记,指定自己的标记语言。然后
生成使用此标记的实际内容流。可以使用 XSL(可扩展样式表语言,以 CSS 为基础)
把内容流转换为其它内容流。
对于基于 PC 的浏览器,HTML 无论在文档内容方面,还是在格式编排方面,都已根深
蒂固。领先的浏览器供应商大量投资基于 HTML 的呈现引擎,同时大量投资基于 HTML、
便于 JavaScript 处理的文档对象模型(DOM)。
XML 看来充当了 PC 浏览器环境中 HTML 文档现有内容的补充角色。
对于新型设备,如支持 WAP 的电话和语音客户机,数据内容和格式是由新兴 XML 架
构(DTD)、用于 WAP 电话的 WML 以及用于语音接口的 VoiceXML 定义的。
对于多数 Web 应用程序设计,您应关注在服务器端使用 XML。有关在服务器端使用
XML 的其它讨论内容,请参阅第 51 页,5.3.4 节的“XML”。
42
5.1.6
JavaScript
JavaScript 是一种跨平台的、面向对象的脚本语言。由于其所支持的浏览器和文档对象,
这种语言在 Web 应用程序中非常实用。客户端 JavaScript 提供与 HTML 窗体交互的功
能。您可以使用 JavaScript 验证客户机的用户输入,并减少经过网络流向服务器的请求
数目,以帮助改进 Web 应用程序的性能。
ECMA,一家欧洲标准团体,
发布了一种以 Netscape 的 JavaScript 以及 Microsoft 的 JScript
(称为 ECMAScript)为基础的标准(ECMA-262)。ECMAScript 标准定义了一组用
Web 浏览器编写脚本的核心对象。JavaScript 和 JScript 实施 ECMAScript 的超集。
为了适应各种客户端要求,Netscape 和 Microsoft 通过添加新的浏览器对象,在 1.2 版中
扩展了对 JavaScript 的实施。由于 Netscape 的扩展与 Microsoft 的扩展互不相同,所以使
用 JavaScript1.2 扩展的任何脚本都必须检测所使用的浏览器,并选择要运行的恰当语句。
一种解释为用户可以禁用客户机浏览器上的 JavaScript,但这可以被程序检测到。
只要有其它 Java 技术替代选择,就不提倡在 Web 应用程序的服务器端上使用 JavaScript。
在您的设计中,表明使用 JavaScript 价值(如简单编辑检查)
的地方,请使用 JavaScript1.1,
其中包含 ECMAScript 标准的核心元素。
5.1.7
Java小程序
Java 小程序具有可在 Web 浏览器中运行的用户界面(UI)技术的最大灵活性。Java 包
含一整套丰富的 UI 元素,其中包括每个 HTML UI 元素的对应元素。另外,由于 Java
是一种编程语言,因而可以创建和使用无限数量的 UI 元素。可以使用许多小配件库,
其中包含通用 UI 元素,例如,表、滚动文本、电子表格、编辑器、图形、图表等。
Java 小程序是采用 Java 编写的程序,可从 Web 服务器上下载,并在 Web 浏览器上运行。
使用 APPLET 标记在 HTML 页中指定要运行的小程序:
<APPLET CODEBASE="/mydir"CODE="myapplet.class"width=400 height=100>
<PARAM NAME="myParameter"VALUE="myValue">
</APPLET>
43
在此实例中,将运行名为 myapplet 的 Java 程序。把数据发送到程序的有效方法是使用
PARAM 标记。该程序可以访问此参数数据,并可容易地将其用作对显示逻辑的输入。
Java 还可以从 Web 应用程序服务器请求新的 HTML 页。这就提供了与 HTML FORM 提
交功能对等的功能。其优点是,小程序可以根据明显的(所点击的按钮)或唯一的(在
电子表格中编辑单元)加载新的 HTML 页。
Java 程序的一个特征是,它们很少只包含一个类文件。相反,大型程序可以引用数百个
类文件。逐个请求每个类文件会给服务器造成严重负担,同时也会突破网络容量极限。
但是,把所有这些类文件包装到一个文件可把请求数目从数百个减少到仅仅一个。此优
化可在许多 Web 浏览器中以 JAR 文件或 CAB 文件的形式使用。Netscape 和 HotJava 只
需在 APPLET 标记内添加一个 ARCHIVE="myjarfile.jar"变量即可支持 JAR 文件。Internet
Explorer 使用在 APPLET 标记内指定为小程序参数的 CAB 文件。在各种情况下,执行
JAR/CAB 文件内包含的小程序将显示比执行单个类文件更快的加载时间。尽管
Netscape 和 Internet Explorer 使用不同 APPLET 标记来识别包装的类文件,但可以创建
一个包含两种标记的 HTML 页来支持这两种浏览器。每种浏览器只需忽略另一种浏览
器的标记。
在 UI 生成中使用 Java 小程序的一个缺点是,Web 浏览器必须支持要求的 Java 版本。因
此,使用 Java 时,应用程序的 UI 部分将决定哪些浏览器可用于客户端应用程序。注意:
领先的浏览器支持 Java 的 JDK1.1 级别的变体,而且具有不同的用于已签名小程序的安
全模型。
Java 小程序的另一个缺点是,当需要那些不包含在浏览器上 Java 支持组成部分的类(如
小配件和商业逻辑)时,必须从 Web 服务器将其加载。如果这些附加的类非常大,初
始化小程序可能需要从几秒钟到几分钟的时间,具体情况取决于连接因特网的速度。
由于上述缺点,在有浏览器混合级别和品牌存在的环境中不提倡使用 Java 小程序。只有
在此情况下,即 HTML UI 元素不足以表达客户机 Web 应用程序用户界面的语义,才可
以使用小程序。如果绝对需要使用小程序,必须小心地尽可能把属于核心 Java 类的 UI
元素包含在内。
44
5.2 移动客户机
移动客户机包含像移动电话、寻呼机和 PDA 这样的无线设备。这些设备作为 Web 客户
机所带来的挑战是支持平台的计算资源非常有限。移动设备通常都非常小,因而在集成
电话、网络计算机和因特网的所有元素时,会提高硬件体系结构的限度。因此,其限制
包括:带宽窄、屏幕小、内存有限以及电能缺乏。但是,目标是要克服这些限制,通过
利用和扩展现有的 Web 服务器体系结构,提供从任何地方访问信息和应用程序的便利。
5.2.1
设备
移动设备包括无线台式 PC、WAP 设备、i-mode 设备、PDA 和语音电话(Phonew/
Voice)。这些设备构成一个设备齐全的远程计算环境,该环境具有采用开放性标准和
新兴技术的高效平台。这些设备通常把移动电话功能与 PDA 功能结合在一起。
PDA 设备无法运行台式 PC 上运行的主要操作系统,因此,有各种针对移动设备的平台。
掌上设备使用 Palm-OS。WinCE/PocketPC 设备使用 Microsoft Windows 的一个称为
Windows CE 的版本。
5.2.2
语音
支持语音的应用程序可实现免提的用户体验,而且不受计算机界面控件限制的妨碍。
语音技术分为两类:识别语音的技术以及生成语言的技术。计算机识别人类语言的能力
称为“自动语音识别”(ASR)。从书面文字生成语音的能力称为语音合成或文本语言
转换(TTS)。
这里只对这个领域进行了宽泛的概述。有关详细信息,请参阅《采用 WebSphere
Everyplace 访问套件的移动应用:设计和开发》编号:SG24-6259。
45
5.2.3
体系结构
支持移动客户机会影响运行时拓扑,因此,必须采用系统体系结构的最佳做法来设计和
实施。有个好消息是,任意投入 Web 体系结构用于支持基于因特网应用的以往投资均
可延伸为支持移动客户机。
任何移动解决方案中,均会有一个网关。在结构上,客户机设备和 Web 服务器之间使
用无线应用协议(WAP)网关。该网关把请求从无线协议转换为 HTTP 请求,相反地,
又把 HTTP 请求转换为适当设备格式。
5.2.4
WAP
WAP 是“无线应用协议”。这是用于信息表示和交付到无线设备的标准,且不受平台、
设备和网络限制。此规范可以扩展并充分利用其它开放性标准,例如,XML、HTML、
IP 和 SSL,并可在多种网络上运行。此协议的目的是通过移动电话、寻呼机和其它无线
设备提供一个用于全球性安全访问的平台。
WAP 的关键特性是因特网编程模型、WML、WMLScript 和无线电话应用(WTA)。
WAP 体系结构定义 OSI 层(如传输、安全、事务处理、会话和应用环境)的协议。它
还包括 API,如 WBXML、WTA 和 WTAI。
5.2.5
微型浏览器
WAP 微型浏览器运行于移动客户机。它们负责显示用 WML 编写的网页,并且可执行
WMLScripts。它们与在 PC 上运行的 HTML 浏览器起着相同的作用。这种浏览器允许请
求 WML 页,并可处理网页之间的导航。尽管许多 WAP 设备包含微型浏览器,但多数
并不支持完整的协议栈。
5.2.6
WML
无线标记语言(WML)以 XML 和 HTML 4.0 为基础,专门用于小型手持设备。它是一
种基于标记的语言,处理格式化静态文本和图像,可接受数据输入以及跟踪超级链接。
46
设计模型把针对单个屏幕的一组相关标记定义为“卡片”。而把一组相关的卡片定义为
“卡片组”。当有页请求时,会把完整的卡片组下载到微型浏览器。用户在卡片之间浏
览,直到需要另一个卡片组上的某个卡片为止。
图像是以无线位图格式(WBMP)处理的。所有支持图像的 WAP 浏览器必须支持此格
式。
5.2.7
WMLScript
这是 WML 的配套语言,就像 JavaScript 是 HTML 的配套语言一样。这种语言可进行过
程编程,如循环、条件句和事件处理。已经为小内存空间和小型设备对信息技术进行了
优化。这种语言源于 JavaScript。
WMLScript 包含一个标准功能库,这些功能包括数学、浏览器访问、核心语言功能。
WML 页不包含 WMLScripts,而是对脚本 URL 的引用。把 WMLScript 文件发送到 WAP
浏览器之前,先将其编译成字节代码。
5.2.8
蓝牙技术
这是一套用于低量程(最多 30 英尺)无线设备的技术规范,定义诸如电能使用和射频
这样的标准。此技术的目标是以一种对等方式,容易而简单地连接大量的计算和电信设
备,旨在作为移动 PC、移动电话和其它便携式设备之间的小规模、低成本和短程无线
电链路的标准。该标准是一个称为 Bluetooth SIG 的公司社团设计的,而 IBM 就是其中
一个创始成员。它之所以能够成为一种通用标准,是因为受欧洲移动电话标准(GSM)
成功的影响,即 GSM 使电话用户可以跨越国界。
该规范分为两部分:核心规范和概要规范。核心规范处理普遍性主题,如无线电、基带、
链路管理器、服务发现协议、传输层和互操作性。概要部分指定不同类型的蓝牙应用所
需的协议和过程。该技术通过与内置加密和验证进行一对一连接,实施三个语音和数据
通道。
47
诸如家庭联网、汽车联网以及移动电子商务这样的应用领域可极大地受益于该项技术。
有关详细信息,请参阅位于以下地址的“普遍计算中的蓝牙应用”白皮书:http://
www-3.ibm.com/pvc/tech/bluetoothpvc.shtml,以及位于以下地址的蓝牙网站:http:
//www.bluetooth.com。
5.3 Web应用程序服务器
图 5-3 显示推荐的 Web 应用程序服务器技术模型。
图 5-3Web 应用程序服务器技术模型
本节中,我们假定您将使用 Web 应用程序服务器和服务器端 Java。尽管还有许多其他
类型的 Web 应用程序服务器模型,但该模型是业内广泛采用的。
48
考察 Web 应用编程环境中可用技术和 API 之前,首先谈谈此节点上的两个基本操作组
件:HTTP 服务器和 Java 虚拟机(JVM)。对于生产性应用程序来说,应针对领域中诸
如稳定性、性能和可用性这样的操作特征来选择这些基本组件。
然后,我们将讨论经常在用户界面中使用的“模型-视图-控制器”设计结构。对于
Web 应用程序编程模型:
最好使用 JavaServer Page 实施“视图”。
交互控制器主要与处理 HTTP 请求以及调用正确商业或 UI 逻辑相关,它通常适用
于作为服务件来实施。
“模型”通过一套 JavaBeans 组件表示到“视图”和“交互控制器”。
5.3.1
Java服务件
服务件是基于 Java 的软件组件,它们可以通过动态生成的 HTML 响应 HTTP 请求。在
Web 请求处理方面,服务件比 CGI 更有效率,因为服务件不会为每个请求创建新的过
程。
服务件在 J2EE 模型定义的 Web 容器内运行,因此可以访问多种基于 Java 的 API 和服
务。在此模型中,HTTP 请求是由像 Web 浏览器这样的客户机使用服务件 URL 调用的。
与该请求关联的参数通过 HttpServletRequest 传递到服务件,HttpServletRequest 以名字
/值对的形式保存数据。服务件通过访问当前 HttpSession 对象维持多个请求之间的状
态,而当前 HttpSession 对象对于每个客户机都是唯一的,并且在客户机会话的整个生
命周期中保持可用。
作为 MVC 控制器组件,服务件把请求任务委托给协调商业逻辑执行的 Beans。然后把
任务的结果转发到“视图”组件,如产生格式化输出的 JSP。
使用服务件的优点之一是,Java 程序员非常容易掌握 API。J2EE1.2 平台规范需要 Servlet
API2.2,以便支持 Web 应用程序的包装和安装。该 API 在摒弃一些方法的同时,增加
了新的方法和常数。
49
IBM WebSphere Application Server 4.0 的关键变化是,现在 HTTP 的会话范围限定在 Web
应用程序上。以前,会话对象的范围覆盖整个 JVM。
服务件是 Web 应用程序编程模型中的一项核心技术。在实施处理从 Web 客户机接收的
HTTP 请求的交互控制器类(Interaction Controller classes)时,建议选择服务件。
5.3.2
JavaServer Page(JSP)
通过把 Web 表示与 Web 内容分离开来,JSP 可以简化创建网页的过程。在 Web 应用程
序的页面构造逻辑中,发送到客户机的响应通常是模板数据和动态生成数据的组合。在
此情形下,与利用服务件相比,使用 JSP 更加容易。因此,JSP 代表着 MVC 模型中的
“视图”组件。
与标准的 Java 服务件相比,JSP 的主要优势是更加接近于表示介质。JavaServerPage 是
作为 HTML 页进行开发的。一旦编译,就作为服务件运行。JSP 可包含 Web 作者熟悉
的所有 HTML 标记。JSP 可能包含 Java 代码段,这些代码段又包含为 HTML 页生成内
容的逻辑。这些代码段可以调出 Beans 访问可重复利用的组件和后端数据。
JSP 技术使用类似于 XML 的标记以及用 Java 编程语言编写的小脚本,为 HTML 页概括
生成动态内容的条件逻辑。通常,首先开发最终 HTML 输出,然后用 JavaScript 逻辑替
换动态部分。在运行时环境中,在 Web 应用程序上执行 JSP 之前,会把其编译成服务
件。输出不仅限于 HTML,而且还包含 WML、XML、cHTML、DHTML 和 VoiceXML。
用于 J2EE 1.2 的 JSP API 为 JSP 1.1。
实施返回 Web 客户机的“视图”时,建议选择 JSP。如果网页所需代码占页面很大的百
分比,并且 HTML 极小,那么,编写一个 Java 服务件将会使 Java 代码阅读和维护起来
更为容易。
5.3.3
JavaBeans
JavaBeans 是 Sun Microsystems 公司开发的一种体系结构,描述 API 以及一组基于 Java
的可复用组件的约定。写到 Sun 的 JavaBeans 体系结构的代码称为 Java Beans 或 Beans。
JavaBeans API 的设计标准之一是支持生成器工具,这些工具用于生成包含 Beans 的解决
方案。Beans 可以是可视的,也可以是非可视的。
50
建议通过以下几种方式将 Beans 与服务件和 JSP 一起使用:
作为模型层的客户机接口。交互控制器服务件将使用此 Bean 接口。
作为其他资源的客户机接口。在某些情况下,可以用某种工具来生成此接口。
作为包含许多属性值对的组件,这些属性值对用于其它组件或类。例如,JavaServer
Page 规范包括一组用于访问 JavaBeans 属性的标记。
5.3.4
XML
可在服务器端使用 XML 和 XSL 样式表对内容流进行编码,并针对不同客户机分析内容
流,这样您就可以针对一系列 PC 浏览器以及新兴普遍设备开发应用程序。内容为 XML
格式,并使用一种 XML 剖析器将内容转换为输出流,而这些输出流以使用 CSS 的 XSL
样式表为基础。
这一通用功能称为“代码转换”,并且不局限于基于 XML 的技术。这里要做出的适当
决策是,在应用程序中您需要对内容转换控制到何种程度。您需要考虑何时适合使用此
动态内容生成,并考虑何时拥有某些设备类型特有的服务件或 JSP 具有优势。
XML 还用作一种指定服务器之间消息内容的手段,无论两个服务器在同一企业内,还
是代表一种企业到企业的连接。这里的关键因素是当事各方在消息模型方面的协议,而
消息模型指定为 XML DTD。XML 剖析器用来从消息流提取特定内容。您的设计需要考
虑是使用基于事件的方法(SAX API 适用),还是使用 DOM API 浏览文档的树状结构。
IBM 向 Apache 开放源码机构捐赠了其 XML4J XML Parser,该机构又把它重命名为
Xerces-java。IBM 还捐赠了其 LotusXSL 处理器,该机构把它重命名为 Xalan-java。
有关开放源 XML 框架的信息,请访问:http://xml.apache.org/。
51
5.3.5
JDBC
在以数据库为中心的情况下,Web 应用程序中的商业逻辑将访问数据库中的信息。JDBC
是一种 Java API,用于独立于数据库的连接。它提供了将 SQL 类型映射到 Java 类型的
简单方法。利用 JDBC,您可以连接到您的关系数据库,并创建和执行用 Java 编写的动
态 SQL 语句。
JDBC 驱动程序是针对 RDBMS 的,由 DBMS 供应商提供,但实施的却是 JDBC API 中
定义的一组标准接口。如果两个数据库之间的模型通用,即可通过更改 JDBC 驱动程序
名称和 URL,在两个数据库之间切换一个应用程序。通常的做法是把 JDBC 驱动程序名
称和 URL 信息保存在属性或配置文件中。
您可以根据应用程序的特征,在四种 JDBC 驱动程序类型中进行选择:
类型 1:JDBC-ODBC 桥式驱动程序(bridge drivers)。这种类型的驱动程序(用
JDK 封装)需要 ODBC 驱动程序,当初推出其的目的是,在无法使用任何其他类
型驱动程序的情况下,使 Java 开发人员能够访问数据库。
类型 2:本机 API 部分 Java 驱动程序。这种类型的驱动程序使用 DBMS 的客户机
API,并且需要用于数据库客户机软件的二进制。这种类型的驱动程序具有性能优
势,但从 JVM 引入本机调用。
类型 3:网络协议全 Java 驱动程序。普通网络协议与此类型驱动程序配套使用。
便携性是该种类型驱动程序的一个主要优势,但也有其局限性,因为它需要中间
件把网络协议转换为 DBMS 协议。
类型 4:本机协议全 Java 驱动程序。这种类型的驱动程序可以移植,并且使用 DBMS
协议。类型 3 和类型 4 的驱动程序非常适用于小程序(小程序访问企业内部互联
网上的数据库服务器),因为小程序只需下载 Java 代码。
增强 Web 应用程序可扩展性的一项重要技术是连接池,该技术可由应用程序服务器提
供。当用户会话中的应用逻辑需要访问某项数据库资源时,代码不是建立新数据库连接
并随后丢弃该连接,而是从已建立的池中请求一个连接,然后在不需要时将其返回到池。
52
如果是根据应用程序拓扑 1 实施解决方案,那么 JDBC 对于您的设计非常重要。与 JDBC
2.0 关联的一项关键技术是 JNDI API。J2EE 要求可以通过 JNDI 命名服务把数据库用作
数据源,而 JNDI 命名服务使代码更加便于移植,更加容易维护。数据源对象提供一个
用于连接池和分布式事务处理的框架,而连接池和分布式事务处理对于企业应用程序极
为重要。程序员可以透明地使用所有这些功能。
JDBC 规范的最新版本为 2.0,但您所使用的许多 JDBC 驱动程序仍然实施 1.0 版。
5.3.6
Enterprise JavaBeans
Enterprise JavaBeans 是 Sun 的 EJB 体系结构(或“组件模型”)的商标术语。当您按
EJB 规范编写代码时,就是在开发“Enterprise Beans”(也可以称为“EJB Beans”)。
Enterprise Beans 与 Java Beans 的不同之处在于,Enterprise Beans 可安装在服务器上,并
可由客户机远程访问。EJB 框架为具有事务处理特征的服务器端组件提供一种标准。
EJB 框架明确规定了 EJB 开发者和 EJB 容器提供者的责任。其意图是使 EJB 容器可以
实施“波导设备”,因为“波导设备”是进行事务处理或数据库访问所必需的。EJB 开
发者指定应用描述符(有时称为“说明性编程”)中 EJB 所必需的事务处理和安全特征。
然后在单独的步骤中,将 EJB 应用到您所选择的应用程序服务器供应商提供的 EJB 容
器。
Enterprise JavaBeans 的类型有两种:
会话
实体
典型的会话 Bean 具有以下特征:
代表单个客户机执行。
可以是事务处理性的。
可以更新基本数据库中的数据。
生命周期相当短暂。
当关闭 EJB 服务器时,会话 Bean 会被毁坏。客户机要继续进行计算,则必须建立
新的会话 Bean。
不代表应存储在数据库中的持久数据。
提供同时执行许多会话 Bean 的可扩展运行时环境。
53
典型的实体 Bean 具有以下特征:
代表数据库中的数据。
可以是事务处理性的。
多个用户可以共享访问。
可以长期存在(与数据库中数据生命一样长)。
即使重新启动 EJB 服务器,也不会丢失实体 Bean。重新启动对客户机是透明的。
为许多同时活动的实体对象提供可扩展的运行时环境。
实体 Bean 一般用于系统重启后仍然存在的信息。而在会话 Bean 中,数据是暂时的,并
且在关闭客户机的浏览器后就不存在了。例如,包含可丢弃信息的购物车使用会话 Bean,
而购物后开的发票则是实体 Bean。
实施实体 Bean 时的重要设计选择是,使用 Bean 管理持久性(BMP)——在这种情况下,
必须对 JDBC 逻辑进行编码;还是使用容器管理持久性(CMP)——在这种情况下,EJB
容器处理数据库访问逻辑。
Web 应用程序的商业逻辑经常访问数据库中的数据。EJB 实体 Bean 就是一种把关系数
据库层包含在对象层的便捷方式,从而隐藏了数据库访问的复杂性。由于单个商业任务
可能需要访问数据库中的多个表,则利用实体 Bean 建立那些表中行的模型,可使应用
程序逻辑操作数据更加容易。
最新的 EJB 规范为 1.1。与 EJB1.0 最大的不同之处在于,1.1 使用了基于 XML 的应用
描述符,并需要供应商实施实体 Bean 支持,从而符合 EJB。
J2EE1.2 平台要求支持 EJB1.1。作为一个工具提供者,IBMWebSphere Application Server
4.0 支持 J2EE,因而也支持 EJB 1.1。EJB 被封装成 EJB 模块(JAR 文件),然后与 Web
模块(WAR 文件)组合在一起,形成 Web 应用程序(EAR 文件)。EJB 应用要求生成
目标应用程序服务器所特有的 EJB 应用代码。
5.3.7
连接器
电子商务连接器是网关产品,它使您能够访问企业、历史应用程序,以及 Web 应用程
序的数据。连接器产品提供用于访问数据库、数据通信、消息发送和分布式文件系统服
务的 Java 接口。
54
IBM 提供一套重要的电子商务连接器,其包含对 CICS、Encina、IMS、MQSeries、DB2、
SAP 和 Domino 的工具支持。IBM 的工具支持以通用连接器框架(CCF)为基础。对于
System 390 上的资源,IBM 目前提供基于 CCF 的本机连接器。
IBM CCF 用作 J2EE 连接器体系结构 (J2C)的基础。J2C 目前是 J2EE 1.3 最终草案的
组成部分。与 CCF 相同,J2C 允许通过通用客户机接口 API(CCI)访问一系列系统。
应用程序设计人员针对单个 API 进行编码,而不是为每个私有系统提供唯一接口。API
与后端系统之间的链接称为资源适配器,并由第三方供应商提供。资源适配器封装为
RAR 文件。这有点像 JDBC 驱动程序的模型。
IBM WebSphere Application Server 4.0 高级版提供一种可安装的运行时支持,该支持以
作为技术预览的 J2C 规范的提议最终草案为基础。高级版中的管理控制台支持“资源适
配器”配置。对 CICS 和 IMS 的支持是随用于 SAP、PeopleSoft 和 J.D.Edwards 的第三
方工具一起提供的。目前,仅支持单阶段确认(single-phase commit)。管理控制台可
以联合用于资源适配器的连接库,这些连接库封装复合属性。组件提供者通过 JNDI 查
找机制,从连接库为 EIS 系统请求连接。
命令 Bean 模型使您可以把代码编写到所选择的特定连接器接口上,同时设法不让 Web
应用程序的其余部分看到连接器逻辑。
如果正在解决方案中实施“直接集成单通道”应用模式,则连接器尤其重要。
5.3.8
Java消息服务(JMS)
JMS API 使 Java 程序员能够访问面向消息的中间件,例如,Java 编程模型中的 MQSeries。
这种消息中间件是访问现有企业系统的流行选择,而且是根据应用程序拓扑 2 实施解决
方案的选项之一。JMS 提供一种可靠、对等、松散耦合的异步通信。消息模型允许进行
点到点通信或发布/订阅。将来的 J2EE 1.3 规范需要 JMS 分布式事务处理(JMS/XA),
以获得两段式确认(two-phase commit)。WebSphere Application Server V4.0 高级版提
供 JMS/XA 与 MQSeries 进行集成。
55
5.3.9
附加的企业Java API
J2EE 1.2 规范定义一组相互配合的相关 API。下面是迄今为止尚未讨论过的 API:
JNDI 1.2:Java 命名和目录接口(JNDI)。此程序包提供一个连接目录服务的通用
API,而且其不受任何目录访问协议的限制。这样就可以容易地迁移到新的目录服
务。通过此接口,组件提供者可以按名称存储和检索 Java 对象实例。服务提供者
工具包括用于 JDBC 数据源、LDAP 目录、RMI 和 CORBA 对象注册的工具。JNDI
的实例应用包括:
-
从 LDAP 目录访问用户配置文件
-
查找和访问 EJB 主页
-
查找驱动程序特定的数据源
RMI-IIOP1.0:远程方法调用(RMI)。RMI 和 RMI over IIOP 作为客户机访问
EJB 服务的访问方法,是 EJB 规范的组成部分。从组件提供者的观点看,这些调
用是本地的。基础结构负责调用远程方法和接收响应。为使用此 API,组件提供者
创建了 EJB 接口的 IDL 描述,然后对其进行编译,以生成客户端和服务器端存根。
存根把对象工具与对象请求代理(ORB)连接起来。ORB 通过因特网 ORB 间协议
(IIOP)彼此通信。RMI 还可用于实施有限功能的 Java 服务器。
JTA 1.0:Java 事务处理 API(JTA)。这一与事务处理服务配套使用的 Java API
以 XA 标准为基础。有了 EJB 服务器,您就不太可能直接使用此 API。
JAF 1.0:Java Beans 激活框架。此 API 不是供一般应用程序使用,但可能是高级
电子邮件交互所必需的。
JavaMail 1.1:这是一组用于支持电子邮件的类。从功能上说,它提供用于读取、
发送和撰写因特网邮件的 API。此 API 模仿邮件发送系统,并需要用于发送邮件
的 SMTP 以及用于接收邮件的 POP3 或 IMAP。提供特殊的数据包装类(data wrapper
classes),用于查看和编辑邮件内容中的数据。对 MIME 数据的支持委托给识别
JAF 的 Bean。
56
5.4 集成服务器
应用程序框架将应用程序集成组件定义为允许不同应用程序之间彼此通信的组件。在此
案例中,我们将把 IBM MQSeries 和 MQSeries Integrator 用作集成服务器,并以这些产
品为基础进行探讨。
5.4.1
面向消息的中间件
在本红皮书中,我们将研究面向消息的中间件(MOM)的角色和用途,这里的 MOM
作为拓扑结构的集成组件。首先,我们将了解 MOM 的原理,然后,使用这些产品说明
MOM 在电子商务模式中的作用,从而考察该产品。
技术原理
要了解 MOM 的作用及其组件技术,我们必须考察 MOM 所回答的问题。
典型后台办公分布式系统环境中对于企业应用程序集成(EAI)的需要提出一个问题,
该问题可分为几个不同的部分:
如何在系统之间传输信息
如何转换信息,以便于一个系统的输出为另一个系统所接受和理解
需要搞清楚的是,这些问题彼此之间有何区别。
很明显,前者要求把网络基础结构应用到位,以便支持传输。另外,在没有 MOM 解决
方案的情况下,应用程序必须包含特定代码,此代码可在网络之间开始恰当连接,并能
通过这些连接收发信息。在典型的 MOM 解决方案中,传输层代表应用程序处理此功能。
如果必须更改某个应用程序的信息输出才能用于接收应用程序,就会使用转换和路由应
用程序。
传输层
为使用 MOM 传输层,发送应用程序以“消息”的形式对其信息进行封装,标注收件人
地址,并把它转交传输层,以便传递到目标收件人。接收应用程序只需连接到传输层并
请求消息,就可以获得消息。使用传输层可以使(但并不要求)接口(消息发送)成为
应用程序之间的异步操作。
57
构成传输层组成部分的每个节点都配置必需的网络连接和其它定义,这使其能够存储消
息,或在可能情况下转发消息。就此而论,我们使用“节点”一词来描述传输层的功能
控制点。
在分布式网络的主机之间传输消息时,传输层封装网络协议和字符编码标准之间的消息
传输事务和可能的转换事务。
传输层还有一个应用程序编程接口(API),它使应用程序只通过与传输层进行会话就
能够发送和接收消息。
传输层不处理任何消息转换。然而,在使一个系统的输出为另一个系统所接受时,可能
会需要进行消息转换。
转换和路由
当一个系统需要另一个系统的输出以不同形式出现时,就需要进行消息转换。转换是由
专用应用程序处理的,该应用程序从传输层获取消息,应用必需的转换,然后把转换过
的消息传回传输层,并发送给最终收件人。
如果需要转换,发送应用程序就将其消息发送到转换应用程序,而转换应用程序根据需
要调整消息,并把其重新路由到目标地址。可以有选择地提取一条传入消息的内容,并
以不同形式路由到许多目标地址。
从该基本概念起,此类最新的专用应用程序把消息范式扩展为提供丰富的功能,而不仅
仅是简单的消息转换和路由。不仅可以像前面方案中描述的那样,以点到点的方式分发
消息,而且可以通过某种代理服务,按“发布和订阅”样式进行分发。
消息代理服务
消息代理,顾名思义,是一种服务。这种服务保存按规定主题发布的消息,从而使客户
机应用程序获取已订阅的消息。这种消息分发方法进一步减轻应用程序的责任,而这些
责任与该应用程序和其它应用程序之间的数据交换相关联。
消息增强和事务处理协调
许多现代转换应用程序都增加了一个功能,即执行数据库操作,作为消息转换和路由过
程的组成部分。
58
在向前发送消息之前,该数据库操作可用于查找以及将更多数据添加到该消息。
更为重要的是,消息中的数据可用于更新数据库内容,作为消息转换和路由过程的组成
部分。这样就不可避免地需要事务处理功能,通过此功能,可以把所有相关转换、路由
和数据库操作(统称为“消息流”)作为一个工作单位进行协调。如果协调成功,就接
受该工作单位;如果任一部分发生故障,就重新运行该工作单位。
5.5 更多信息
有关本章所述主题的更多信息,请参阅:
《使用 WebSphere Application Server 版本 4.0 的自助服务模式》,编号:SG24-
6175
《使用 WebSphere 高级版的 CCF 连接器和数据库连接》,编号:SG24-5514
《WebSphere 版本 4 应用程序开发手册》,编号:SG24-6134
《具有 WebSphere Everyplace Access Suite 的移动应用程序:设计和开发》编号:
SG24-6259
Flanagan、David 和 JavaScript:《权威指南》第三版,O'Reilly 和 Associates 公司,
1998 年
Maruyama、Hiroshi、Kent Tamura 和 Naohiko Uramoto:《XML 和 Java:开发 Web
应用程序》Addison-Wesley,1999 年
Flanagan、David、Jim Farley、William Crawford 和 KrisMagnusson:《Nutshell 中
的 Java Enterprise》O’Reilly 和 Associates 公司,1999 年
http://www.ecma.ch/stand/ECMA-262.htm ECMAScript 语言规范
http://java.sun.com/products Java APIs 和技术
http://www.ibm.com/software/ebusiness/docs/index.html
IBM 电子商务策略论文软件包括:
-
《电子商务 IBM 应用程序框架:精明技术选择》
-
《电子商务 IBM 应用程序框架:结构概览》
http://validator.w3.org/Validator 工具
http://jigsaw.w3.org/css-validator/Validator 工具
http://www.bluetooth.com Bluetooth 网站
59
泛计算中的 Bluetooth 应用程序白皮书 http://www.ibm.com/pvc/tech/
bluetoothpvc.shtml
http://xml.apache.org/开放源 XML 框架
60
6
应用程序设计
为了说明创建使用路由器和分解的自助服务程序中使用的技术,我们为此项目创建一个
示例应用程序。本章将讨论用于创建此应用程序的 Java 设计技术。
此处介绍的信息旨在补充《使用 WebSphere Application Server 4.0 的自助服务模式》编
号:SG24-6175。我们以那本书介绍的应用程序设计为基础,将设计延伸为包括消息发
送功能。为此,我们非常重视 Sun 的 Java 消息服务(JMS)在企业消息发送应用程序中
的作用。我们将探讨支持 API,如 Sun 的 Java 命名和目录接口。另外,还将介绍 WebSphere
的 JMS 侦听器,而 JMS 侦听器是 WebSphere 企业服务中扩展的消息发送支持的组成部
分。
本章最后将从实例应用程序抽取一个应用案例,并详细讲述该应用程序设计。
61
6.1 JMS概述
Sun Microsystem 的 Java 消息服务(JMS)是定义 Java 应用程序与企业消息发送中间件
(如 MQSeries)如何进行交互的 API。本节假定基本了解消息发送系统涉及的概念,并
着重介绍如何把这些概念映射到 JMSAPI。
6.1.1
消息模型
首先,了解消息发送模型的不同类型非常重要。每种模型在为该模型定义专用操作的
JMS 中有一组接口。有两种体系结构可用于消息发送应用程序:点到点(PTP)和发布
/预订(pub/sub)。
点到点应用程序是根据以下观点创建的:每个消息均以特定队列为其地址。接收端应用
程序从该队列获取消息,并对其进行相应处理。在 JMS 中,PTP 类型带有“队列”前
缀,如下表所示。
JMS 父级
PTP 类型
ConnectionFactory
QueueConnectionFactory
Connection
Que ueConnection
Destination
Queue
Session
QueueSession
MessageProducer
QueueSender
MessageConsumer
QueueReceiver
发布/预订应用程序则是按照以下思想创建的:根据消息内容路由消息。消息被发送到
消息代理,该消息代理根据其内容把消息路由到适当订阅者。然后,订阅者就可对该消
息进行适当处理。在 JMS 中,发布/预订类型带有“主题”(Topic)前缀。
在本书中,我们重点考察 PTP 应用程序。
62
6.1.2
消息组件
由于 JMS 是为创建、发送和接收消息而存在,因而您会认为消息是 JMS 的核心组件。
JMS 消息由以下几部分构成:
标题:包含识别和路由消息的信息。
属性:可以有选择地添加到消息的定制值。属性可以是:
-
应用程序特定的:添加到消息的属性由 JMS 应用程序使用。
-
标准:JMS 属性。
-
提供者特定的:某个消息提供者特有的属性。
正文:数据。
6.1.3
消息类型
JMS 提供五种不同专业的消息,以提供访问每个消息内容的不同操作。图 6-1 显示这
些类型。
图 6-1 JMS 消息类型层次
不同消息类型包括:
BytesMessage:包含访问字节流的操作。此消息类型将与任何现有消息格式相匹配。
StreamMessage:包含访问 Java 原始值流的操作。
ObjectMessage: 包含访问序列化 Java 对象的操作。如果应用程序设计需要序列
化一个以上的对象,那么就请使用“集合”(Collection)对象。
63
MapMessage:包含从消息正文访问一组键值对的操作。关键字必需为字符串,值
必须为原始类型。
TextMessage:包含访问作为字符串的消息正文的操作。在实例应用程序中,我们
把 TextMessage 用作传输消息(用 XM 进行编码)的手段。
6.1.4
目标类型和子类型
下一个要掌握的概念是“目标”类型和子类型。基本上,目标是消息的容器。消息从一
个应用程序发送到目标,然后由另一个应用程序从目标中将其删除。
熟悉 MQSeries 的人也应该熟悉术语“队列”,而队列就是消息的目标。JMS 把队列称
为 PTP 应用程序中目标的一种特殊类型。按照消息发送模型,目标既可称为队列(用于
PTP),也可称为主题(用于发布/预订)。
另 外 , 每 个 消 息 发 送 模 式 有 一 个 更 加 专 业 化 的 目 标 类 型 : TemporaryQueue 和
TemporaryTopic。这些类型为在“连接” (Connection)期间存在的目标。
图 6-2 显示目标层次结构。
64
图 6-2 JMS 目标类型层次
下面是 JMS API 中其他关键字类型的摘要:
分别使用 MessageProducer 和 MessageConsumer 将消息发送到目标或从目标接收消
息。在 PTP 模型中,子类型 QueueSender 将消息发送到目标,而 QueueReceiver
从目标接收消息。
MessageProducer 和 MessageConsumer 是使用“会话”(PTP 模型中的 QueueSession)
创建的。会话还提供各种消息类型的库方法和临时目标(例如,TemporaryQueue)。
会话实例是使用连接创建的。连接代表着与 JMS 提供者的连接。在 PTP 模型中,
子类型为 QueueConnection。
最后,连接是使用 ConnectionFactory(PTP 模型中的 QueueConnectionFactory)创
建的。ConnectionFactory 将是建立消息发送应用程序的起点。
这只是对 JMS 的简单概述。有关 JMS 的更多信息,请参阅“Java 消息服务”规范,可
从以下地址下载:
http://java.sun.com/products/jms/index.html
65
6.2 JMS和JNDI
为了提高消息发送应用程序的可移植性,JMS 依靠 Sun 的 Java 命名和目录接口(JNDI),
可向 Java 应用程序提供命名服务。
JMS 具有所谓的“管理对象”,管理对象具有两种类型:ConnectionFactory 或 Destination。
管理员可以把具有这两种类型的对象放在 JNDI 命名空间中,供消息发送应用程序进行
访问。
图 6-3 显示与 Java 应用程序相关的 JMS 和 JNDI 的角色。这两种 API 高于任何特定服
务提供者,并且封装任何特定供应商的信息。
因此,在支持消息发送的应用程序中使用这些技术的开发人员只需熟悉 API,而无需熟
悉具体的消息发送系统。
图 6-3 与应用程序相关的 JMS 和 JNDI 的角色
6.2.1
什么是JNDI?
Java 命名和目录接口(JNDI)提供一种从 Java 应用程序中访问目录和命名功能的手段,
并且不受任何特定目录提供者工具的限制。
通俗地说,JNDI 好似 Java 应用程序的电话簿。名称与信息关联,而信息采取对象的形
式。在 JNDI 中,与对象关联的名称称为绑定。绑定存储在一个上下文内。
66
上下文像某个城市的电话簿。曼哈顿的电话簿存储居住在曼哈顿的人们的电话号码。要
查找芝加哥某个人的电话号码,我们需要命名系统中不同的上下文。JNDI 没有绝对命
名方法的概念。所有名称都与某个上下文相关。
命名系统是一组具有相同类型的上下文。这样,纽约州的所有电话薄就是一个命名系统。
而所有电话簿中的所有名称的集合就是命名空间。即命名空间是命名系统中所有名称的
集合。
命名服务的其他实例包括电子邮件、URL 和文件系统。
当然,上面提供的关于 JNDI 的信息已足以能够帮助您理解 JNDI 利用 JMS 能有多大帮
助。有关更多信息,请参阅《Java 命名和目录(JNDI API)》,并可从以下链接下载:
http://java.sun.com/products/jndi/index.html
6.2.2
为何使用JNDI?
JNDI 不是 JMS 的要求,那么,使用 JNDI 有什么好处?
Sun 设计 JMS 规范的目标之一就是可移植性。使用 JNDI,开发人员不必考虑如何连接
到消息发送系统这样的细节,而且,无需把这些细节嵌入应用程序。
开发人员只需要知道应用程序所依赖的 JMS 对象的 JNDI 名称即可。系统管理员负责创
建适当绑定。使用此策略,JMS 应用程序将不受 JMS 提供者的限制。
请看实例 6-1,该实例连接一个队列,并发送简单的文本消息。在此实例中,我们将说
明不使用 JNDI 的弊端。
实例 6-1:不使用 JNDI 的编码实例
String QMGR ="";
String QUEUE ="SYSTEM.DEFAULT.LOCAL.QUEUE";
QueueConnectionFactory factory =null;
factory =new MQQueueConnectionFactory();
((MQQueueConnectionFactory)factory).setQueueManager(QMGR);
QueueConnection qCon =factory.createQueueConnection();
QueueSession qSession =qCon.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
67
Queue q =qSession.createQueue(QUEUE );
QueueSender qSender =qSession.createSender(q );
TextMessage message =qSession.createTextMessage("To the Bat-Queue!");
qSender.send(message );
qCon.close();
第二个代码块需要加以注意。
JMS 应用程序中的第一个步骤是获取 QueueConnectionFactory 实例。注意:
在不使用 JNDI
的 实 例 中 , 我 们 需 要 创 建 新 的 MQQueueConnectionFactory 实 例 , 该 实 例 实 施
QueueConnectionFactory 接口。这会存在许多方面的不便。
首先,看一下对高度依赖于特定供应商信息(如上例所示)的消息发送应用程序进行开
发和维护所需要的角色:
角色 1:需要创建适当队列和队列管理器的管理员。
角色 2:开发、测试和应用消息发送应用程序的开发人员。
在上面的实例中,开发人员必须知道关于队列的特定管理信息,例如,队列管理器的名
称以及队列的名称。可以用属性文件的信息使此信息具体化,但是,如果其中任何信息
发生变化,开发人员(或负责维护应用程序的任何人)就需要在适当地方更新此信息。
使用 JNDI,开发人员就不再依赖于特定管理信息,而只需要知道如何从 JNDI 命名空间
获取对象。管理员可以更改队列管理器和队列的名称,而且,只要 JNDI 名称保持不变,
就不会影响消息发送应用程序。
其次,在讨论 JMS 应用程序中使用特定供应商的类的弊端时,要考虑 Sun 的可移植性
目标 — 这一目标在上述实例中并未实现。假定要在具有许多消息发送平台的企业中部
署应用程序。如果使用上述实例中的约定部署此应用程序,就会需要开发、测试、应用
和维护多个应用程序版本。
68
最后,就像我们在实例应用程序中看到的一样,使用 JMS 的 WebSphere 应用程序需要
在 JNDI 命名空间中注册适当对象。换句话说,我们必须具有一些在命名空间中注册的
对象,否则,消息发送应用程序就无法工作。
6.2.3
JMS如何使用JNDI
JMS 通过管理对象利用 JNDI。管理员按照每个供应商特有的方式把对象放在 JNDI 命名
空间中。
那 么 ,JNDI 命 名 空 间中 可以放置什么呢?管理对象可以是以下两种类型之一:
ConnectionFactory(图 6-4)或 Destination(第 65 页的图 6-2)。管理员可以把属于
这两种类型的任何 JMS 对象放在该命名空间之中,供应用程序进行访问。
图 6-4 JMS ConnectionFactory 层次结构
实例 6-2 显示如何重新设计第 67 页的实例 6-1 以支持 JNDI。此实例假定管理员已在
JNDI 命名空间中放入一个绑定到名称“ivtQCF”的 QueueConnectionFactory 以及一个绑
定到名称“ivtQ”的 Destination 队列。
实例 6-2:支持 JNDI 的 JMS 实例
java.util.Hashtable environment =new java.util.Hashtable();
environment.put(Context.PROVIDER_URL,"iiop://localhost");
69
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.ejs.ns.jndi.CNInitialContextFactory");
Context ctx =new InitialContext(environment );
QueueConnectionFactory factory =(QueueConnectionFactory)ctx.lookup("ivtQCF");
QueueConnection qCon =factory.createQueueConnection();
QueueSession qSession =qCon.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Queue q =(Queue)ctx.lookup("ivtQ");
QueueSender qSender =qSession.createSender(q );
TextMessage message =qSession.createTextMessage("To the Bat-Queue!");
qSender.send(message );
qCon.close();
提示:在上面的实例中,没有特定供应商的类。而且,代码中没有像队列管理器名称和
队列名称这样的信息。所有信息都封装在管理员提供的管理对象中。
那么,管理员是如何把这些对象放在 JNDI 命名空间呢?如前所述,此步骤是供应商特
定的。在 MQSeries 中,MA88 SupportPac 具有管理称为 JMSAdmin 的管理对象的工具。
6.3 WebSphere到MQSeries连接选项
从应用程序服务器放在 MQSeries 队列上的消息可能直接产生于某个服务件,也可能是
从某个命令 Bean 或 EJB 发送来的。无论哪种方法,都会使用可用的 MQSeries Java API
把该消息发送到队列管理器。每个 API 均具有使其适合于某种情形的某些特征,具体取
决于您所拥有的优先权。 但是,选择 API 可能会对用于分发应用程序组件的选项产生
影响。
下面我们讨论两种 API:
用于 Java(MQ base Java)包的 MQSeries 类, com.ibm.mq.jar。MQ 基本 Java 使
Java 小程序、应用程序、服务件和 EJB 能够向 MQSeries 发出呼叫和查询。
70
用于 Java 消息服务(MQ JMS)包的 MQSeries 类, com.ibm.mqjms.jar。MQ JMS
实施 Sun 的 Java 消息服务 (JMS),从而使 JMS 程序能够访问 MQSeries。
如果应用程序可移植性、供应商独立性和位置透明性都很重要,那么 JMS 就是技术选
择明显的优胜者。JMS 使用消息发送概念摘要提供独立于供应商的消息发送 API,而
MQSeries 在其下面实施了 JMS 接口。使用目录命名服务(MQSeries 消息服务),把作
为 MQSeries 队列管理器和队列的真正实体 - 对象 - 定义到 JMS。MQ JMS 支持 JMS
的点到点和发布/预订模型。这是我们在实例中所使用的 API。
MQ base Java 和 MQ JMS 提供到达 MQSeries 的两个连接选项:
直接连接到队列管理器的绑定模式
使用 TCP/IP 连接到队列管理器的客户机模式
两个连接选项都支持连接池。
6.3.1
Java绑定模式
从 Java 到 MQSeries 的最快链接是使用 Java 绑定模式。它提供到达 MQSeries 队列管理
器的直接连接,而 MQSeries 队列管理器与应用程序驻留在同一主机上。此案例中的关
键连接参数是队列管理器名称。
图 6-5 Java 绑定模式
71
连接到本地队列管理器有多个重要优势。首先,与远程队列管理器的连接相反, 在您
自己的主机上建立到达队列管理器的连接的可能性会非常高。其次,避免在建立到达队
列管理器的网络连接时所花费的时间。第三,本地队列管理器可以在多个代理之间分配
工作。在您的网络中,如果连接性能优先,那么使用绑定模式是必然选择。
使用绑定模式,针对支持 XOpen/XA 标准的数据库和驱动程序,您还可以把 MQSeries
用作需要 MQSeries 更新和数据库更新的工作单位的 XA 资源协调者。
Java 客户机模式
用于 Java 的 MQSeries 类以及用于 JMS 的 MQSeries 类以客户机模式提供与 MQSeries
的连接。这类似于绑定模式,与队列管理器的连接是通过服务器连接通道进行的,即应
用程序可以连接到其他主机上的队列管理器。关键连接参数是主机名、TCP/IP 端口、
以及服务器连接通道名称。
如果不想使 MQSeries 与应用程序服务器驻留在同一设备上,则最好使用客户机模式。
客户机模式使您可以直接连接到远程 MQSeries 队列管理器。
图 6-6 到达远程代理的客户机模式
当直接连接到某个代理上的队列管理器(如图 6-6 所示)时,您将放弃队列管理器提
供的任何工作负载分配。应用程序必须决定把工作发送到哪个代理,并且一切工作负载
分配必须在应用程序本身中进行,而这种方法是不提倡的。即使把队列管理器放在群集
中也无济于事,因为队列管理器总是把工作发送到代理的本地实例。另一个缺陷是,
XOpen/XA 为对 MQSeries/JMS 协调的承诺提供便利,而且无法通过将 MQSeries 用
72
作事务处理协调者来使用数据库。
解决该问题的一种方法是连接到没有代理实例的远程队列管理器,但也只是用于工作负
载分配,如图 6-7 所示。
图 6-7 到达远程队列管理器的客户机模式
仍然存在网络连接时间,事实上,由于引入了中间系统使得事情更加麻烦。但是,您确
实拥有队列管理器工作负载分配的优势,以及连接到远程队列管理器的能力。而另一种
方法是使用 TCP/IP 负载平衡,但这并不是 MQSeries 的功能,而且不在本书探讨范围
之内。
凭借经过内部 TCP/IP 栈,客户机模式还可用来连接到本地队列管理器。很明显,这不
如使用绑定模式方便,但它确实使您可以在一般环境中使用程序,而您不必知道队列管
理器是否是本地的。还可以通过参数来确定不同连接选项,这样,不管连接类型为何,
应用程序都处于就绪状态。无需确保传递正确的参数,就可以获得所需的连接类型。如
果您正在与某个数据库进行交互,数据库表就是一个很好的选择。
MQ JMS 和 MQ 基本 Java 都允许您把消息从 WebSphere 中的 Java 应用程序直接放到远
程代理的队列中。如果您考虑这样做,您应同时考虑性能问题。创建网络连接的成本将
添加到每个请求的总成本中。对于每个请求,都创建 MQ 到客户机的会话。不存在长久
的网络连接,这会影响同时运行数千个会话的能力。如果您为每个请求创建本地 MQ 会
话,开销将就会低得多。网络连接现在是通过发送端-接收端通道对维持的,并且持续
相当长时间。最好是持续时间长的 MQ 会话。在 MQSeries 的下一个版本中,创建本地
会话的开销可望得到显著降低,因而会进一步拉大客户机模式和绑定模式之间的性能差
73
距。
6.3.2
把WebSphere用作事务处理协调者
用于 Java 消息服务的 MQSeries 类包括 JMS XA 接口。这些接口允许 MQ JMS 参与事务
处理管理器协调的两段式确认,该事务处理管理器符合 Java Transaction API (JTA)。
使用这些类,WebSphere Application Server 高级版可以在全局事务处理中协调 JMS 发送、
接收操作,以及数据库更新。
WebSphere 提供一对附加的管理对象,这样 MQ JMS 就可以与 WebSphere 集成:
JMSWrapXAQueueConnectionFactory
JMSWrapXATopicConnectionFactory
您可以与使用 MQQueueConnectionFactory 和 MQTopicConnectionFactory 完全相同的方
式使用这对对象。 但是在幕后,它们使用 JMS 类的 XA 版本,并使 MQ XAResource
参与 WebSphere 事务处理。
如果某个事务处理中使用一个以上 XAResource,WebSphere 协调者仅调用真正的两段
式确认。调用单项资源的事务处理是使用一相优化承诺的。这在很大程度上消除了在分
布式和非分布式事务处理中使用不同 ConnectionFactory 的必要。
有关详细信息,请参阅《使用 Java 的 MQSeries》编号:SC34-5456-06。此手册包含
在 MQSeries 产品中。
6.4 处理异步消息
JMS 侦听器是随 WebSphere Enterprise Services 封装在一起的定制服务。它可侦听用于消
息的 JMS 目标。当某个消息发送到监视目标时,侦听器通知称为消息 Bean 的专用
Enterprise JavaBean,消息 Bean 指派为处理对该特定目标的请求。JMS 侦听器角色的图
形表示如图 6-8。
74
图 6-8 使用 JMS 侦听器的应用程序间的消息流
请将上面的消息流与图 6-9 中的消息流进行比较。图 6-9 描述两个不使用侦听器的应
用程序之间的消息流。
图 6-9 不使用侦听器的应用程序之间的消息流
在图 6-9 中显示的情形下,接收端应用程序知道它将从给定目标接收消息。但是,并
不知道何时接收以及接收的频繁程度。因此,接收端应用程序有两个从目标获取消息的
选项:
1.
它可以进行呼叫以接收消息,并无限期等待。当消息最终到达时,呼叫应用程序
重新获得对进程的控制。
2.
它可以使用呼叫接收消息并指定超时。一旦达到超时或从目标收到消息,呼叫进
程重新获得对进程的控制。
这一问题的解决方案是使用 JMS 侦听器。这消除了开发人员对处理从目标到达的异步
消息的逻辑进行编码的必要。此逻辑封装在侦听器内,这样接收到消息后,开发人员就
可以专注于必须开发的逻辑。
侦听器的每个实例都映射到目标和消息 Bean。目标是受到监视的队列。消息 Bean 是接
收消息时调用其 onMessage()方法的类。
75
6.4.1
消息Bean
当从目标接收消息时,JMS 侦听器调用映射到目标的消息 Bean 中的 onMessage()方
法,从而传递接收到的消息。
与 WebSphere 的 WebSphere Enterprise Services 中 JMS 侦听器一起使用的消息 Bean,是
对 Enterprise JavaBean (EJB) 2.0 规范中定义的消息驱动型 Bean 的早期改编。
WebSphere V4 中的 EJB 符合 EJB 1.1 规范,因此,使用 1.1 规范开发的任何消息 Bean
都应把最终迁移到 2.0 规范的必要考虑在内。在下面几段中,我们首先讨论使用 1.1 规
范开发消息 Bean 的现行方法。接下来,我们将讨论消息驱动型 Bean 合同以及如何设计
要迁移到要迁移到 2.0 规范的 1.1 Bean。
在 1.1 规范中,消息 Bean 只不过是包含商务方法 - onMessage() - 的无国籍会话
Bean。此方法为何如此特别?
onMessage()方法可在类型 javax.jms.MessageListener 上找到(请参见图 6-10)。在
JMS API 中,MessageListener 注册到 Session 或 MessageConsumers 以便异步通知它们传
入的消息。
图 6-10 javax.jms.MessageListener 接口
尽管它们不实施 MessageListener,我们创建的消息 Bean 必须具有同样的签名。
消息 Bean 必须具有以下组件。
远程接口
接口必须定义具有与 javax.jms.MessageListener 相同签名的 onMessage()方法(Throws
子句除外)。要进行的逻辑事务似乎会扩展 MessageListener 接口;但是,远程接口中的
所有方法必须丢弃 RemoteException。因此,该接口必须扩展 javax.ejb.EJBObject。
76
定 义 的 商 务 方 法 必 须 遵 循 RMI 的 规 则 ; 因 此 , 商 务 方 法 必 须 丢 弃
java.rmi.RemoteException。
远程接口应看起来像实例 6-3 的样子:
实例 6-3:远程接口实例
public interface Example Message extends javax.ejb.EJBObject{
public void onMessage(Message message)throws RemoteException;
}
主接口
该接口必须扩展 javax.ejb.EJBHome。方法必须遵循 RMI 规则;因此,方法应丢弃
java.rmi.RemoteException。另外,Throws 子句还应包含 javax.ejb.CreateException。
该接口还必须定义一个不包含任何参数的 create() 方法,并具有远程接口的一个返回
类型。尽管会话 Bean 可以定义一个以上的 create()方法(这些方法必须在 Bean 类中
具有相应的 ejbCreate()方法),2.0 规范中的消息驱动型 Bean 只能有一个不包含任
何参数的 create()方法。
实例 6-4 显示主接口看起来应是什么样子。
实例 6-4:主接口
public interface ExampleMessageHome extends javax.ejb.EJBHome{
public ExampleMessage create()throws CreateException,RemoteException;
}
Bean 类
该类必须实施 javax.ejb.SessionBean。该类必须是公用的,而且不能是最终的或抽象的。
最后,该类还必须实施商务方法和 ejbCreate()方法。
6.5 应用程序结构
本书的讨论和实例主要以名为 Guild of Weather Masters 的实例应用程序为基础。此应用
程序最初是在《使用 WebSphere Application Server V4.0 的自助服务模式》编号:SG24
-6175 中介绍的。从位于以下地址的电子商务模式网站下载 PDK-Lite 3.0 版,即可获
得该应用程序:
http://www.ibm.com/framework/patterns
我们将把该应用程序扩展为包括消息发送功能。本节将逐个介绍各应用案例以阐释应用
程序设计。然后,详细讲述一个案例来说明所使用的技术。有关与使用 JMS 无直接关
77
系的许多组件的详细信息,请参阅《使用 WebSphere Application Server V4.0 的自助服务
模式》编号:SG24-6175。
6.5.1
模型-视图-控制器
自助式服务 Web 应用程序可视为浏览器和 Web 应用程序服务器之间的一组交互。这些
交互始于浏览器对应用程序欢迎页(welcome page)的初始请求。这通常是由用户在浏
览器上键入欢迎页 URL 完成的。用户通过单击按钮或链接,启动随后的所有交互,这
样就把请求发送到 Web 应用程序服务器。Web 应用程序服务器处理该请求,动态生成
结果页,并将其发送回客户机,同时带有一组按钮和链接以进行下一个请求。
仔细考察这些交互,就会发现一组需要在服务器端考虑的通用处理请求。这些交互可以
很容易地映射到标准的模型-视图-控制器 (MVC)设计模式。
模型:我们称之为商业逻辑。它代表实施应用程序数据和商业逻辑的应用程序对
象。我们建议用 JavaBeans 或 Enterprise JavaBeans 包装商业逻辑。这样就可以把
商业逻辑与 Web 特定的交互控制器和显示页逻辑分离开来,从而把商业逻辑与
Web 编程的细节隔离开来,进而增强商业逻辑在 Web 应用程序和非 Web 应用程序
中的复用性。
视图:我们称之为页面构造者。视图负责编排应用程序结果的格式,并负责生成
要返回客户端的 HTML 页。尽管视图可以用 JSP 和/或服务件实施,JSP 允许直
接用 HTML 开发模板页,并为页面的动态元素插入脚本逻辑以及为多部分页面插
入 jsp:include 操作。因此,JSP 是实施构造页面组件的最佳选择。
控制器:我们称之为交互控制器。交互控制器是将独立于协议的商业逻辑与 Web
应用程序联接在一起的代码段。这就是说,交互控制器的主要责任是把 HTTP 协
议特定的输入映射到独立于协议的商业逻辑(此商业逻辑可能由若干不同类型的
应用程序使用)要求的输入,将商业逻辑的各个元素用脚本编写在一起,然后委
派给页面构造组件,该页面构造组件将创建返回到客户机的响应页。
78
建议一般使用服务件来实施交互控制器。但是,对于不涉及条件逻辑或事务处理逻
辑的简单应用程序,可以把交互控制器和页面构造逻辑组合成一个组件。在这种情
况下,JSP 是最佳选择。
在《使用 WebSphere Application Server V4.0 的自助服务模式》编号:SG24-6175 中,
详细讲述了 MVC。这里只是说明我们讨论的技术如何适合该体系架构。
6.5.2
实例应用程序应用案例
原始应用程序以如下假设为基础:存在一个称为 Guild of Weather Masters 的机构。该
Guild 有一笔总基金,可以把其中的资金(外太空) 转到各个站(行星)。只有 Guild
管理员可以转移资金,而所有用户均可显示 Guild 的资金余额或每个站的资金余额。
我们将扩展此应用程序,通过增加新的功能,使所有用户均可请求日志,该日志包含已
执行的转帐和一个或多个站余额。
图 6-11 应用案例
我们的应用案例有两个角色,站管理员和 Guild 管理员。
79
站管理员可以显示 Guild 资金余额和各站资金余额,并请求显示各站转账记录的报表。
Guild 管理员可以显示 Guild 资金余额和各站资金余额,并可把资金从 Guild 转移到站,
还可以请求显示各站转账记录的报表。
6.5.3
显示资金
该应用程序首先从后端数据库检索 Guild 资金余额以及各站资金余额,并把它们提交给
用户。
图 6-12 显示资金流程
视图:index.jsp 是进入应用程序的入口。此页上的链接“单击此处可获得最新余
额”会把您引导到 DisplayFundsServlet (/PDK)。
控制器:DisplayFundsServlet:代表交互控制器。服务件负责调用适当的命令 - 在
此案例中,命令为 GetAllBalancesCommand。
执行该命令之后,服务件用具体实例说明视图 Bean (DisplayBalancesViewBean),
并把从该命令检索的余额值存储在视图 Bean 中。
一旦“水合(hydrated)”视图 Bean,或用适当值填充视图 Bean,就将视图 Bean
添加到请求对象。最后,服务件把请求转发到适当 JSP。
80
模型:GetAllBalancesCommand 代表从控制器到服务件的桥路。
实施 GetAllBalancesCommandImpl 命令将引用 GuildFinanceSessionBean,这是该应
用程序数据访问的正面。
会话 Bean 使用代理模式把请求转发到 GuildAccounts 实体 Bean 和 StationsFunding
实体 Bean,以获取 Guild 余额和站余额。
模型:GuildAccountsBean 和 StationsFundingBean:代表商业逻辑的简单 Java Beans。
它们都实施 getBalance()公用方法,该方法返回账户的当前值。
视图:DisplayBalancesViewBean 由 DisplayFundsServlet 创建并保存 JSP 所需的结
果。
视图:DisplayFundsRichResults.jsp:代表显示页。它是构造响应页的 JSP,该响应
页显示 Guild 账户余额以及每个气象站的余额。
6.5.4
转移资金
用户可以从 DisplayFundsRichResults JSP 中查看 Guild 余额以及各站余额。可以使用字
段将资金从 Guild 转移到您所选择的站。
用户选择适当的站并输入要转移的金额。单击“转帐”按钮后,交易即可执行。
81
图 6-13 转移资金流程
一旦用户请求要转移的资金,就会发生以下情况:
控制器:TransferFundsServlet 代表交互控制器。
首先,服务件从用户那里检索表格信息。然后,服务件从 CommandFactory 获取
TransferFundsCommand 实例,执行从 Guild 到所选站的资金转移。
一旦执行该命令,服务件就从 CommandFactory 获得 LogTransactionCommand 实例,
该实例记录这项交易。
一旦记录交易,服务件就把请求转发到 transferFundsSuccessful JSP。
模型:TransferFundsCommand 调用 GuildFinanceSessionBean 中的 guildGrantFunds
()方法。
-
模型:GuildFinanceSessionBean 调用两个方法。首先,它调用 GuildAccountsBean
实体 Bean 中的 attemptWithdrawal()方法。然后,调用 StationsFundingBean
实体 Bean 中的 attemptDeposit()方法。每个实体 Bean 针对其关联数据库执
行请求的函数。
82
-
控制返回到 TransferFundsServlet。
控制器:TransferFundsServlet 调用 LogTransactionCommand 把交易记录发送到交易
日志数据库。以 XML 格式构造交易信息,并使用 JMS 把包含此信息的消息放在
MQSeries 队列 (SOLARSYS.INPUTQ)上。
-
MQSI 从该队列检索消息,并执行 SOLARSYS_TRANSLOG 消息流。此消息
流在 TRANSLOG 数据库的 LOGENTRIES 表中为交易创建条目。SOLARSYS
_TRANSLOG 消息流将在第 239 页的 14.2:“创建 SOLARSYS_TRANSLOG
消息流”中讲述。
-
控制返回到 TransferFundsServlet。
视图:transferFundsSuccessful JSP 用于表示转帐成功。transferFundsFailed JSP 用于
表示转帐失败。从这些显示中,用户可以单击链接,以便检索新的余额。单击此
链接,使控制返回到 DisplayFundsServlet,DisplayFundsServlet 检索新的余额并显
示它们。
6.6 应用程序技术
视图报表应用案例是对此应用程序中使用的所有技术很好的说明。我们将深入考察用于
实施此应用案例的应用程序设计和编码技术。
83
图 6-14 发送和接受消息
6.6.1
视图报表应用案例概述
首先,让我们看一下结构,因为它与 MVC 模型相关。在我们的实例应用程序中,我们
已经确定需要检索显示站余额和 Guild 余额的报表,以及检索执行的交易。我们通过向
MQSI 发送请求(把消息放在 MQSeries 队列上),可以使用命令来检索此数据。此命令
将在本地执行(迄今为止,尚未使用命令传送)。
视图:displayFundsRichResults JSP 包含一个链接,该链接使您可以查看包含各站
余额以及交易记录的报表。当用户单击“查看报表”时,就会发生以下情况。
控制器:ViewStatementServlet 调用 ViewStatementCommand 命令 Bean 处理请求。
模型:ViewStatementCommand 创建一条包含请求信息的 XML 消息,并使用 JMS
把该消息放在
SOLARSYS.STATEMENT.REQUEST MQSeries 队 列 中 。
ViewStatementCommand 在 SOLARSYS.STATEMENT.REPLY 队列上等待回复。
84
-
MQSI 获得该请求,把它分成三个单独的请求,并将这些请求发送到三个不同
目标。
对站余额的请求是在 SOLARSYS.STATEMENT.STATION.REQUEST 队
列上发送的。此目标是 WebSphere Application Server,在这里,一个 JMS
侦听器检索此消息,并调用 StationRequestListener 消息 Bean 的 onMessage
()方法。该 StationRequestListener 是一个没有国籍的会话 Bean。它使
用 JDBC 从 STATIONS 数 据 库 检 索 请 求 的 数 据 。 响 应 在
SOLARSYS.STATEMENT.AGG.Q 上返回。
提示:消息 Bean 的角色应只是一种从队列获取消息的手段。 一旦删除,就可能有多种
处理方法,但任何处理都不应由消息 Bean 进行。一旦消息 Bean 收到消息,就会把消息
路由到知道如何处理消息的类。
我们的应用程序就是一个简单案例,其中 Bean 接收到的所有消息都以同样的方法进行
处理。这很简单,因为该 Bean 知道每次需要传递到同样的类。该类与任何其他相关类
共同对消息进行相应的处理。
如果 Bean 可以收到需要以不同方法处理的各种消息,事情就比较复杂。因为,Bean 必
须知道如何把消息路由到不同的类。建议的解决方案是使 Bean 把消息交给某种 Factory,
该 Factory 知道如何根据消息中的信息把消息路由到适当对象。
将
对
Guild
余
额
的
请
求
发
送
到
SOLARSYS.STATEMENT.GUILD.REQUEST 队 列 。 此 目 标 是 同 一
WebSphere Application Server 。 JMS 侦 听 器 检 索 此 消 息 , 并 调 用
GuildRequestListener 消息 Bean 的 onMessage()方法。JMS 侦听器使用
JDBC
从
GUILD
数 据 库 检 索 请 求 数 据 。 其 响 应 在
SOLARSYS.STATEMENT.AGG.Q 上返回。
第三个请求在 SOLARSYS.RATECALC.REQUEST 队列上发送。目标是后
端历史系统,在这里,CICS-MQ 桥把请求传递到 CICS。其响应在
SOLARSYS.RATECALC.ACK 队列上返回。当处理 CICS 时,在将消息
发送回 SOLARSYS.STATEMENT.AGG.Q 之前,需要进行一些额外的处
理工作。这将在第 269 页的 14.3.4“PROCESS_RATECALC_CICS_
ACK”中讲述。
85
然 后 , MQSI 流 把 响 应 聚 集 成 一 个 回 复 , 并 将 其 发 送 回
-
SOLARSYS.STATEMENT.REPLY 队列上的 ViewStatementCommand。
视图:在此应用程序中,视图由从回复直接发送到浏览器的 XML 构成。如果此数
据最终发送到用户,这就不是有用的显示。但是,由于结果是 XML 文档,因而,
我们可以利用几种技术为该用户编排数据格式。
-
为与应用程序中的其他视图保持一致,我们可以分析该 XML,使用数据填充
视图 Bean,并把数据发送到将要显示的 JSP。这是一种非常完美的解决方案,
但是,这种解决方案的代价是:
我们需要开发具有足够智能的对象,以便根据 XML 数据构造视图 Bean。
我们需要花费额外的处理时间和资源。
-
把数据从 XML 转换为视图的另一个选项是可扩充样式表语言(XSL)。实际
上,XSL 是创建 XSL 样式表的语言。而这些样式表又可以把 XML 转换为另一
种格式(例如,HTML)。
XSL 不在本书讲述的范围之内。有关详细信息,请参阅位于以下地址的 XSL
W3C 建议:http://www.w3.org/TR/xsl/,以及位于以下地址的 Xalan(一
种 XSL 转换(XSLT)处理器):http://xml.apache.org/xalan-j/index.html。
6.6.2
视图:DisplayFundsRichResults
此应用案例的应用程序流假定已执行“显示资金”应用案例流,以及可能的“转移资金”
应用案例流。此时,用户将查看显示各站及 Guild 的资金状态的 DisplayFundsRichResults
JSP。
“视图”和“控制器”之间的接口是通过使用视图 Bean (DisplayBalancesViewBean)
实现的。视图 Bean 是把数据发送到 JSP 的手段。结果 Bean 是从命令返回数据的手段。
在此情况下,该命令扮演结果 Bean 的角色,并在命令执行时自动填充数据。然后,服
务件从该命令中删除数据,并水合(填充)视图 Bean,该视图 Bean 发送到视图。
实例 6-5:DisplayFundsRichResults.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
86
<%@page session="false"info="Patterns"errorPage="WEB-INF/error.jsp"%>
<%@page import="com.ibm.pdk.*"%>
<!-- This bean encapsulates the data from the back end -->
<!—此Bean压缩后端数据 -->
<jsp
:
useBean
id="guildViewBean"type="com.ibm.pdk.view.DisplayBalancesViewBean"
scope="request"/>
用户可以单击窗口底部的“视图报表”链接以检索资金状况报表。单击此链接即可调用
ViewStatementServlet。
实例 6-6:调用控制器
<center><a href="/PDK/ViewStatementServlet">View Statement</a></center>
6.6.3
控制器:ViewStatementServlet
用于应用程序的交互控制器是通过 ViewStatementServlet 实施的。该交互控制器调用使
用命令 Bean 的商业逻辑。服务件为:
实例化命令
设置命令属性
调用命令的执行方法
检索命令的结果
实例 6-7:ViewStatementServlet
//Instantiate command(实例化命令)
ViewStatementCommand command=
( ViewStatementCommand ) CommandFactory.instance ( ) .getCommand ( CommandFactory.VIEW _
STATEMENT_COMMAND);
writeToLog("Obtained ViewStatementCommand");
//Set command target(设置命令目标)
command.setCommandTarget ( CommandTargetFactory.instance
(CommandTargetFactory.LOCAL_COMMAND_TARGET));
(
)
.getCommandTarget
//Set command properties(设置命令属性)
command.setUserRole(role );
command.setStation(station );
//set interest rate info (this should be put into a db somewhere)
(设置利率信息<此信息应放在某个数据库中>)
InterestRate rate =new InterestRate();
rate.setCreditRating(0 );
rate.setDuration(48 );
rate.setType("NEW");
rate.setYear("2001");
87
command.setInterestRate(rate );
//execute the command(执行命令)
command.execute();
//send the results to the user(把结果发送给用户)
writeToLog("Command executed.Putting results to UI");
response.getOutputStream().println(command.getMessage());
response.getOutputStream().flush();
}catch(Throwable theException){
// uncomment the following line when unexpected exceptions(当意外情况发生时,解出以下
代码行的注释)
// are occurring to aid in debugging the problem.(有助于调试问题。)
theException.printStackTrace();
}
6.6.4
使用命令调用商业逻辑
应用程序中的下一步是命令执行。其中包含三个步骤:
创建命令接口
执行命令
执行目标
创建命令接口:ViewStatementCommand
命 令 接 口 通 过 TargetableCommand 接 口 而 扩 展 , 如 果 需 要 , 还 可 通 过
CompensableCommandInterface 扩展。在我们的实例中,ViewStatementCommand 说明最
常见的实施方法,即实施用于可确定命令的命令接口。
实例 6-8:ViewStatementCommand
package com.ibm.pdk.command;
import com.ibm.websphere.command.*;
import com.ibm.pdk.*;
public interface ViewStatementCommand extends TargetableCommand {
public StationsBalanceList getBalances();
public InterestRate getInterestRate();
String getMessage();
public String getStation();
public TransactionMap getTransactions();
public String getUserRole();
public void setBalances(StationsBalanceList balanceList);
public void setInterestRate(InterestRate rate);
public void setStation(String station);
88
public void setUserRole(String role);
}
显而易见,ViewStatementCommand 定义多个命令特定的方法。
此外,TargetableCommand 接口说明如表 6-1 显示的方法。
表 6-1:TargetableCommand 方法
方法
目的
setCommandTarget()
指定命令的目标对象。
getCommandTarget()
返回命令的目标对象。
setCommandTargetName()
指定命令的目标名称。
getCommandTargetName()
返回命令的目标名称。
hasOutputProperties()
表明命令是否具有必须复制回客户机的输出。实施
类还提供 setHasOutputProperties()方法,用于设
置 此 方 法 的 输 出 。 在 默 认 情 况 下 ,
hasOutputProperties()返回“真”值。
setOutputProperties()
保存来自命令的输出值,以便返回到特定客户机。
performExecute()
封装应用程序特定的工作,并由命令接口中声明的
execute()方法调用。
应 用 程 序 开 发 人 员 必 须 实 施 的 唯 一 方 法 即 performExecute ( ) 方 法 。
TargetableCommandImpl 类实施其余方法,以及命令接口中声明的 execute()方法。
执行命令:ViewStatementCommandImpl
如 果 要 传 送 某 个 命 令 , 则 必 须 通 过 扩 展 TargetableCommandImpl 类 来 实 现
TargetableCommand 类 。 该 类 实 施 TargetableCommand 接 口 中 的 所 有 方 法 , 但
performExecute()方法除外。此方法必须由应用程序开发人员编写。它还实施来自命
令接口的 execute()方法。
继续我们的实例,先看命令执行 ViewStatementCommandImpl 的责任。
89
定义实例和类变量
ViewStatementCommandImpl 类声明方法在类中使用的变量。
实例 6-9:定义实例和类变量
public
class
ViewStatementCommandImpl
extends
ViewStatementCommand {
private StationsBalanceList balances;
private com.ibm.pdk.InterestRate interestRate;
private java.lang.String station;
private java.lang.String userRole;
private com.ibm.pdk.TransactionMap transactions;
private java.lang.String message;
/**
*ViewStatementCommandImpl constructor comment.
*/
public ViewStatementCommandImpl(){
super();
}
TargetableCommandImpl
实施命令特定的方法
ViewStatementCommandImpl 实施命令特定的方法。
实例 6-10:实施命令特定的方法
public StationsBalanceList getBalances(){
return balances;}
public com.ibm.pdk.InterestRate getInterestRate(){
return interestRate;}
public java.lang.String getMessage(){
return message;}
public java.lang.String getStation(){
return station;}
public com.ibm.pdk.TransactionMap getTransactions(){
if(transactions ==null ){
setTransactions(new TransactionMap());}
return transactions;}
public java.lang.String getUserRole(){
return userRole;}
public void setBalances(StationsBalanceList newBalances){
balances =newBalances;}
public void setInterestRate(com.ibm.pdk.InterestRate newInterestRate){
interestRate =newInterestRate;}
public void setStation(java.lang.String newStation){
station =newStation;}
public void setUserRole(java.lang.String newUserRole){
userRole =newUserRole;}
90
implements
实施命令接口方法
必须实施来自命令接口的两个方法:isReadyToCallExecute()和 reset()。
实例 6-11:实施来自命令接口的方法
public boolean isReadyToCallExecute(){
return true;}
public void reset(){
setBalances(null);
setInterestRate(null );
setStation(null );
setTransactions(null );
setUserRole(null);}
实施 TargetableCommand 接口方法
需要实施一个 TargetableCommand 方法:performExecute()。覆盖 setOutputProperties
()的默认实施也可能是适当的,因为 setOutputProperties()不保存最终的、临时的
或静态的字段。
实例 6-12:实施来自 TargetableCommand 接口的 performExecute()
public void performExecute()throws Exception {
Hashtable environment =new Hashtable();
envienvronment.put(
Context.PROVIDER_URL,PdkProperties.singleton().getProviderURL());
environment.put(
Context.INITIAL_CONTEXT_FACTORY,
PdkProperties.singleton().getInitialContextFactoryClassName());
Moore code here to send and receive message from MQSeries
这里有更多需要从MQSeries发送和接收的消息
writeToLog("Message received from reply q![message="+replyMessage+"]");
setMessage(replyMessage.getText());
writeToLog("Closing");
qCon.close();
}
91
执行目标:LocalCommandTarget
作为 TargetableCommand 目标的对象必须实施 CommandTarget 接口。此对象可以是实际
服务器端对象,如实体 Bean,也可以是用于某个服务器的客户端适配器。CommandTarget
接口的实施者负责确保在所需目标服务器环境中正确执行命令。
在实例中,我们实施 CommandTarget 接口作为本地目标,该接口在客户机的 JVM 中运
行。要接受命令,该接口必须实施 CommandTarget 接口的单一方法:executeCommand
()方法。
实例 6-13:LocalCommandTarget
public class LocalCommandTarget implements CommandTarget,Serializable {
public TargetableCommand executeCommand(TargetableCommand command)throws CommandException
{
try {
command.performExecute();
writeToLog("Command executed");
return command;
}catch (CommandException ce){
ce.printStackTrace();
throw ce;
}catch(Exception e){
e.printStackTrace();
throw new CommandException(e);
}
}
6.6.5
将消息放在队列上和检索消息
该应用程序需要从 MQSeries 队列发送和接收消息。我们的实施假定 MQSeries 以本地方
式安装在 WebSphere 主机上。由于本地 MQSeries 队列管理器是群集的一部分,因此其
承担了确定目标队列准确位置的任务。它还使我们可以利用 MQSeries 工作负载分配以
及确定的交付功能。
如前所述,消息放在来自命令 Bean - ViewStatementCommandImpl- 的 MQSeries 队
列上。现在,我们看看实际把消息放在这个队列上的 performExecute()方法中的代码。
performExecute()方法负责创建 XML 消息,把它放在队列上,并等待响应,该响应包
含要向用户显示的数据。此方法使用以下输入属性:
92
实例 6-14:输入属性
provider.url=iiop://localhost
initial.context.factory=com.ibm.websphere.naming.
WsnInitialContextFactory
input.Queue.name=SOLARSYS.INPUTQ
qcf.jndi.name=gwmQCF
txn.record.xml.tag.name=transactionrecord
txn.date.xml.tag.name=transdate
station.xml.tag.name=station amount.xml.tag.name=amount
初始化
实例 6-15:建立与 MQSeries 的连接
//Getting initial context(获取初始上下文)
InitialDirContext context =null;
context =new InitialDirContext(environment);
String qcfJndiName =
PdkProperties.singleton().getQueueConnectionFactoryJNDIName();
writeToLog("Get QCF from JNDI[name="+qcfJndiName +"]");
QueueConnectionFactory qConFactory =
(QueueConnectionFactory)context.lookup(qcfJndiName);
建立连接
第一步是创建基本传输 MQSeries 的连接。该连接为临时队列提供范围和位置,在该位
置可保存控制如何连接到 MQSeries 的参数(例如,队列管理器名称,以及使用 MQSeries
Java 客户机连接性的远程主机的名称)。
对于点到点情形,连接是使用 QueueConnection 建立的。
实例 6-16:建立与 MQSeries 的连接
String qcfJndiName =
PdkProperties.singleton().getQueueConnectionFactoryJNDIName();
writeToLog("Get QCoF from JNDI[name="+qcfJndiName +"]");
QueueConnectionFactory qConFactory =
(QueueConnectionFactory)context.lookup(qcfJndiName);
//Getting queue connection(建立队列连接)
QueueConnection qCon =qConFactory.createQueueConnection();
93
建立会话
会 话 提 供 产 生 和 使 用 消 息 的 上 下 文 , 其 中 包 括 用 于 创 建 MessageProducer 和
MessageConsumer 的方法。会话还包含 HCONN(队列管理器的句柄),因此可定义交
易范围。
至于我们的点到点连接,会话是使用 QueueSession 建立的。
实例 6-17:建立会话
// Get queue session(获取队列会话)
QueueSession qSession=qCon.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
发送消息
MessageProducer 用来发送消息。它包含对象句柄(HOBJ),其可定义用于写入和读取
的特定队列。对于点到点连接,这是通过 QueueSender 实现的。
下面的代码创建要发送的 XML 消息,并把该消息放在队列上。
实例 6-18:创建消息并把消息放在队列上
String requestQName =PdkProperties.singleton().getRequestQueueJNDIName();
String replyQName =PdkProperties.singleton().getReplyQueueJNDIName();
writeToLog("Getting queues from JNDI namespace[requestJNDIName="
+requestQName +"][replyJNDIName="+replyQName +"]");
Queue requestQueue =(Queue)context.lookup(requestQName );
Queue replyQueue =(Queue)context.lookup(replyQName);
writeToLog("Building queue sender.");
QueueSender qSender =qSession.createSender(requestQueue);
//Creat message(创建消息)
TextMessage requestMessage =qSession.createTextMessage();
String message =getRequestString();
requestMessage.setText(message);
requestMessage.setJMSReplyTo(replyQueue);
requestMessage.setJMSCorrelationID("BobLuvsGoats");
writeToLog("Message built[xml="+message +"]");
//Send message(发送消息)
qSender.send(requestMessage);
//The reply’s Correlation ID should match this MessageID - so capture it
(回复的“Correlation ID”应匹配该 MessageID,这样就可对其进行捕获)
String messageId =requestMessage.getJMSMessageID(); writeToLog("Correlation ID of sent
message ="+messageId);
94
检索消息
MessageConsumer 用来接收消息。就像 MessageProducer 一样,MessageConsumer 包含
一个对象句柄 (HOBJ),该对象句柄定义用于写入和读取的特定队列。对于点到点连
接,这是通过 QueueReceiver 实现的。
实例 6-19:检索回复
//flip the connection to receive mode//把连接转换为“接收”模式
qCon.start();
//用一个msg选择器设置q接收器,以便把请求连接到此msg
String selector ="JMSCorrelationID =\'"+messageId +"\'";
QueueReceiver qReceiver =qSession.createReceiver(replyQueue,selector);
writeToLog("QueueReceiver built -msg selector[selector="+selector+"]");
long timeout =PdkProperties.singleton().getReplyTimeout();
writeToLog("Waiting for reply[timeout="+timeout +"]");
TextMessage replyMessage =(TextMessage)qReceiver.receive(timeout);
if (replyMessage ==null){
writeToLog("ERROR:No reply message received!");
setMessage("No message received");
return;
}
writeToLog("Message received from reply q![message="+replyMessage +"]");
setMessage(replyMessage.getText());
writeToLog("Closing");
qCon.close();
}
提示:
在规定时间,每个 HCONN 只能进行一项操作,因此,不能同时调用与 Session 关
联的 MessageProducer 或 essageConsumer。这与 JMS 限制相一致,即每个 Session
一个线程。
PUT 可以使用远程队列,而 GET 则只适用于本地队列管理器上的队列。一般 JMS
接口细分为多个用于点到点和发布/预订行为的具体版本。
Connection 是 线 程 安 全 的 ( thread safe ) , 但 Session 、 MessageProducer 和
MessageConsumers 则不是。建议策略是每个应用程序线程使用一个 Session。
95
96
7
设计MQSI消息流
MQSI 具有设计和开发消息流的功能,因而您可以将请求路由到后台系统,以便根据应
用程序要求进行检索、聚集和/或更新信息。使用 MQSI 控制中心,您可以创建消息流,
并可将其应用到一个或多个 MQSI 代理。
本章讲述如何设计 MQSI 消息流以便执行这些功能。第 237 页的第十四章“创建 MQSI
应用程序”将详细讲述使用这些功能的应用程序的完整实例。
97
7.1 MQSI应用程序设计
毫无疑问,我们需要清楚地了解需要消息流完成的商业操作。逻辑设计以高超的水平描
述各种要求,并为定义 WebSphere 应用程序组件和 MQSI 域之间的操作合同提供关键性
帮助。针对 MQSI 实施满足操作合同要求的解决方案涉及若干消息路由和转换步骤。同
时,根据您企业的具体情况,可能需要不止一个代理程序。
我们使用的实例应用程序具有以下特征:
接受来自 WebSphere 的请求,以便减轻从 Guild 向各站传输单元时记录交易的负
担。
接受来自用户界面层的请求,以便创建可返回和显示的活动报表供用户使用。
在 MQSI 中,每个转换和路由过程都是作为“消息流”开发的。消息流创建为一系列的
操作,每项操作都采取节点的形式。各种类型的节点都具有相应的连接器,允许直观地
定义节点的输入和输出。
为了完成应用程序所要求的任务,我们需要能够执行以下操作的消息流:
通过输入队列从用户处获取请求
把消息分成若干后端请求
接收后端回复
应用转换规则,并使消息相互关联
执行数据库操作,以便缓存或增强数据
将回复消息返回用户,供其使用
在 MQSI 中可以使用各种节点创建消息流,许多消息流具有高度可配置的特性。消息流
应用到某个代理程序的执行引擎,根据在消息流中开发的商业规则,其可接收和处理消
息。
如若设计复杂的消息流,则考虑其在较广泛的上下文应用程序内所起的作用很有帮助。
实际上,消息流就是有关文档交换的所有情况,其中涉及诸如文档转换、数据库操作之
类的操作。
98
进入 MQSI 中的开发环境并将一组大量相互连接的节点集中以执行全面而复杂的消息转
换,是非常简单的事情。遗憾的是,此类消息流开发方式将来会导致极高的维护费用。
可通过一般输入和输出终端配置消息流,这样,其他消息流就可将该消息流作为其组件
进行重复利用。这些类型的消息流不是应用到代理程序,而是作为大型消息流的组成部
分。
金科玉律:各个消息流组件越简单越好!此后可比较容易地测试和使用这些消息流组件。
如果消息流包含许多节点及更多连接,跟踪其中的故障就会非常困难。
7.1.1
与应用程序的设计合同
作为高级概念,应用程序与 MQS 的合同定义要提供的消息文档、有哪些预期操作以及
预期的回复文档。该合同包括诸如是同步还是异步执行一项操作这样的详细信息。
该过程将从 MQSI 端接收消息以启动所需操作。这些操作可能包括以回复形式存在的检
索和返回信息、更新其他系统中的数据,或根据合同细节对上述情况的任意组合。
7.1.2
消息流结构
为处理此合同而应用的完整消息流可获取传入文档,并执行所需的系列功能,这些功能
负责完成合同中规定的商业操作。
开发标准文档的需要非常明确,这些标准文档支持 MQSI 和 WebSphere 应用程序之间的
通信。但是,要完成整个工作,可能需要把原始文档多次转换为其他形式,从而执行各
项操作。为此,开发文档标准还有很大的好处,即支持子消息流之间的交互。
可将基于标准传入文档类型的通用数据库操作保存为消息流组件。为使用该类组件,必
须定义通用文档转换并将其保存为消息流组件。
99
下面让我们看看作为 MQSI 消息流组件部分,如何将建模的商业操作链接以形成完整的
商业流程。例如,具有“事务日志”文档类型的传入消息可能需要某些转换,才能创建
可用于执行数据库操作的中间文档。
还可能创建这样的消息流:可分析传入消息的内容并将该消息路由到能够使用该类消息
的子消息流。
7.1.3
定义文档类型
要使消息流组件彼此兼容,必须定义在消息流组件间交换的文档类型。定义必须详细描
述将用来解释消息的分析程序,以及消息本身的结构和内容。
对于 XML 文档,是通过使用文档类型定义(DTD)实现的。在本红皮书中,我们假定
读者了解一些有关 XML 的基本知识,并且使用 DTD。有关 XML 的信息,请参见《XML
文件:在企业到企业和企业到消费者应用中使用 XML》编号:SG24-6104。
实例 DTD
下面是 XML DTD 的简单实例。实例 7-1 中的 DTD 描述名为“视图-报表-命令”的
文档。请注意 DTD 如何定义构成该“视图-报表-命令”的数据元素。
实例 7-1:XML DTD
<!--view_stmt_cmd.dtd -->
<!ELEMENT view-statement-command(user,interest-rate+)>
<!ELEMENT user (station,role)>
<!ELEMENT station (#PCDATA)>
<!ELEMENT role (#PCDATA)>
<!ELEMENT interest-rate (type,year,credit-rating,duration)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT credit-rating (#PCDATA)>
<!ELEMENT duration (#PCDATA)>
此 DTD 中有两个子元素:用户和利率。每个元素都包含其他元素。例如,用户元素包
含站元素和角色元素,并在用户元素下面定义。定义一直维持这种情况,直到使用一种
给定数据表示方式定义叶元素(数据本身)。这里显示的所有元素都具有类型#PCDATA,
该术语的意思是“可分析的字符数据”,它基本上是一个字符串。
100
7.2 消息流组件
MQSeries Integrator 主要以基于 MQSeries 产品提供的消息传输层为基础。MQSI 内工作
的消息流必须把 MQSeries 消息作为其输入,并可能产生一条或几条 MQSeries 消息作为
其输出。
MQSI 具有编程接口,它使您可以创建提供新消息处理功能或替换现有功能的新节点。
您还可以创建新的消息分析程序,从而访问 MQSI 未定义的消息类型。
但是,应注意,MQSI 是作为可扩展的消息代理程序框架而设计的,而且将来此框架可
能用于从非 MQSeries 的其他来源获取输入。例如,考虑这样一个 MQSI 节点的潜力,
该节点可把直接输出流从某个输入流馈送到 EJB。
由于此可扩展性,建议经常访问位于以下地址的 MQSeries 系列 SupportPacs 网页:http:
//www.ibm.com/software/ts/mqseries/txppacs/,从而了解有关支持产品扩展的
最新信息。
至于本红皮书,我们将会把 MQSeries 消息作为用于应用程序服务器和应用程序路由器
之间进行通信的媒体,并以此为基础进行探讨。
消息流作为逻辑连接的事件序列进行实施,这些事件以消息流节点的形式进行定义。每
个节点执行消息流中的一个特定功能。MQSI 包含一组消息流节点,它们称为“IBM 基
元”,而且具有种类繁多的功能。
本章介绍以下消息类型:
MRM
NEON
XML
BLOB
我们开发的应用程序使用 XML 消息,这也是我们讨论的焦点所在。
我们还将讨论以下基元节点类型:
转换节点
数据库节点
逻辑控制节点
可复用消息流
101
此外,我们还将讨论提供分解和重组功能的节点类型。这些节点是通过 IBM MQSI
Aggregator 插件提供的。
7.2.1
消息类型
流经系统的消息通常包含特定格式和内容。在消息发送术语中,称之为“消息模板”。
此模板拥有描述消息中所包含数据结构的信息。可使用 MQSI 中的功能定义多种消息模
板,这些功能可定义在转换操作中使用的消息模板信息。也可使用 MQSI 内的定制 XML
消息,这种方法日益普遍。
至于 MQSI 内的转换和编程,许多节点类型可以使用一种非常有效的内部 ESQL 语言。
ESQL 可用于交换管理结构化消息或数据库表的内容。这在处理以 XML 格式或其他消
息格式保存的消息内容时,功能尤其强大。
MQSI 可配置为接受多种消息类型。作为起点,MQInput 节点的 MQSI 可以接受包含简
单字符串消息正文的 MQSeries 消息。
但要在 MQSI 内使用,则 MQSI 的许多可用分析程序中必须有一个可以接受该消息。发
送端应用程序负责包含充足的信息,从而使 MQSI 能够利用其消息。为此,可在标准的
MQSeries 消息正文开头包含另一个称为 MQRFH2 标题的消息标题,这第二个消息标题
包含 MQSI 可用来解释消息正文的详细信息。如果没有 MQRFH2 标题,MQSI 就尝试
选择最适合的分析程序来解释消息内容。
有关 MQRFH2 标题中可以使用的结构和选项的详细信息,请参见《MQSeries Integrator
编程指南 2.0.2》编号:SC34-5603-02。
在完全分析位流当前部分之后,每个分析程序都有决定接下来需要什么分析程序的作
业。如果当前分析程序提名代理程序不可用的分析程序作为其后继者,则使用输入节点
上定义为“消息域”的默认设置。
102
消息集和 MRM
在 MQSI 2 版本中,消息存储库管理器(MRM)是“控制中心”中的一个内置图形工具,
它用于定义要在消息流中使用的结构化数据和消息定义(消息模板)。
通过定义可整理为复合数据类型的各个数据元素,可以定义可复用的数据结构,并可将
这些数据结构用作构造完备的消息布局中的构件,因而能够快速进行复杂消息结构的
GUI 驱动型开发。这样的结构可以组合为各个超集,称为“消息集”。
MRM 的独特优点是,通过导入现有的已定义消息结构(用 C 或 COBOL 定义),能够
创建消息集内容。
MRM 中定义的数据结构可与一个以上分析程序共同使用。这样就可以将同一逻辑数据
结构与不同物理表示一起使用。两个主要选项是:
定制线路格式(Custom Wire Format) (CWF),其中,分析器接受/创建结构
化文档,在此文档中,用给定数据类型(在字符串数据的情况下,还用“长度属
性”)定义每个字段。
可扩展标记语言(XML),其中,分析器接受/创建 XML 文档,该文档具有 MRM
结构描述的窗体。这里需要注意的是,这与我们后面将要讲述的一般 XML 分析程
序不同。
如果消息结构中某个地方也提供长度值,则支持可变长度字符串。
在 MRM 中定义消息定义,并保存为消息集时,消息流节点可利用控制中心的拖放编程
功能。
尽管 MRM 具有一些强大功能,但在某些应用程序中使用 MRM,会限制在处理消息字
段格式时所必需的灵活性。例如,在 MQSI 2.0.2 中,目前不支持标记分隔数据或不支持
长度字段的可变长度字符串。
NEON 格式化程序
MQSI 2 版以基元节点的形式提供 MQSI 1 版中拥有的 NEON 规则和格式化程序功能。
这就为目前实施 MQSI 1 版产品的客户提供了直接迁移路径。
NEON 格式化程序是功能强大的数据格式定义工具,具有与消息集及 MRM 相似的功能。
当需要分析标记分隔输入时,此工具尤其有用。
103
XML
MQSI 的最大优势之一是能够分析和管理用 XML 文档形式表示的消息数据。
MRM 中定义的消息结构可以作为 XML 消息进行分析,但这具有内在的局限性,因为
MRM 主要用来提供与历史系统数据格式的兼容性,这就使 XML 在其灵活的定制数据
表示方式中可提供的优点无法得以发挥。
为此,可以使用一般 XML 文档分析程序,该分析程序能够分析任何标准格式的 XML
消息正文。尽管可提供 MQRFH2 标题来明确地把消息正文声明为 XML,但不必如此,
因为 MQSI 会自动检测传入的消息正文是否为 XML 消息。
作为数据表示新标准,XML 的采用速度非常快。XML 是一种真正的便携式数据交换媒
体,因而是 Web 应用程序中数据表示的自然选择。为此,我们在自助实例中选择使用
一般 XML 消息格式。
MQSI 2 版中目前存在的限制是,无法以图形方式单独完成基于一般 XML 文档的转换节
点编程。但是,完全可以用 MQSI 节点编程语言 ESQL 对这样的转换逻辑进行编码。事
实上,这种方式更为容易。
BLOB
如果没有提供分析程序,或提供的分析程序不可用,BLOB 就是默认分析程序。BLOB
分析程序有两个元素:
一个元素称为 BLOB(其 ESQL 值类型为 BLOB)。这包含可以引用和操作的位流。
另一个元素是 UnknownParserName。由于没有与该元素关联的格式,因此,要使
用该元素,首先必须理解位流。
最好定义一个 MRM,这样就可按名称引用——与位流中的偏移相反。如果您自己解释
位流,要牢记其位流本质。
104
7.3 IBM基元节点
在实施任何消息流之前,MQSI 控制中心会制作一组基本节点,称之为 IBM 可用基元。
这些基本节点是基本构件,将其进行组合可构成完整的消息流。
每个基元节点都有一组输入和输出终端,这些终端可用于把给定节点的输出连接到另一
个节点的输入。例如,MQInput 节点就是一种可配置为从 MQSeries 队列获取消息的节
点。MQInput 节点具有输出终端,可以把该终端连接到计算机节点(可配置功能的另一
种类型,可促进消息转换)的终端。
在《使用控制中心的 MQSeries Integrator 2.0.2 版》编号:SC34-5602-03 一书中,详
细讲述可在 MQSeries Integrator 中使用的全部基元节点。在这里,我们主要讨论创建自
助模式实例时发现的有用节点和功能。多数消息流中一般都可以找到这些节点(聚合节
点除外):
MQInput
计算
数据库节点
过滤器
MQOutput
MQReply
可复用消息流
聚合节点支持包 IA72
7.3.1
MQInput节点
MQInput 节点是所有消息流的开端。MQInput 节点从其分配的队列接收消息,并把消息
通过其输出节点传递到另一个节点的输入。
105
图 7-1 有多个子消息流的 MQInput 节点
如图 7-1 所示,MQInput 节点是消息流的开端,但是,可在 MQInput 输出终端与多个
节点的输入终端之间建立连接。
这样,传入的消息就可以启动一个以上的事件流。例如,您可能会获取一条用来携带某
客户余额请求和个人利率查询的消息,并通过多个子消息流把该消息发送出去,每个子
消息流用来访问不同的后台系统。
7.3.2
转换节点
转换节点执行获取输入消息、并根据需要将其映射到输出消息的任务。
106
计算节点
计算节点是可从调色板上使用的最通用转换节点之一。它可创建输出消息,并可用来执
行几乎所有数据操作。为此,计算节点可能会需要一个以上输入数据源。显然,主要输
入源是经由计算节点输入终端的传入消息。计算节点有选择地把数据库作为其功能的组
成部分。
计算节点拥有接收输入消息的输入终端,及两个输出终端:一个处理正常操作下的输出,
另一个在转换过程发生故障时重新定向原始消息。
可通过计算节点的“属性”窗口配置计算节点,如图 7-2 所示。
让我们看看“属性”窗口的布局。主窗口分为两个部分:输入消息和输出消息。计算节
点可执行的最简单操作是把输入消息复制到输出消息。这可通过选中“复制整个消息”
单选按钮来实现。选中“复制消息标题”单选按钮可自动生成代码,该代码可将消息正
文之前的输入消息的各部分复制到输出。
为执行这些任务而生成的代码显示在 ESQL 选项卡中。
107
图 7-2 计算节点窗口
在每个案例中都会生成一个注解行,建议您把可添加到节点的任何定制代码添加到该注
解行下面。之所以这样,是因为可使用 GUI 构造转换细节。这些操作的结果会影响节点
属性中包含的 ESQL 代码。自动生成的代码保持在注解行以上;用户代码可在注解行之
下输入。
使用“控制中心”配置计算节点时,您很快会发现 ESQL 窗格的大小有限,这使读取节
点背后的代码极为困难。使操作 ESQL 较为容易的技巧是使用 Ctrl+a 选中节点后面的所
有代码,再用 Ctrl+c 复制该选定内容,然后使用 Ctrl+v 把内容粘贴到您喜欢的文本编辑
器中进行编辑。代码更改完成后,可以把代码复制/粘贴回 ESQL 窗格。
ESQL 窗格的功能之一是在您键入时会动态验证代码语法。如果您选择在此窗格中编辑
代码,当代码中出现语法错误时,代码窗格下就会出现一个红色“X”。当您在代码的
结构内尝试新的技巧或功能组合时,此功能非常有用。
108
使用 ESQL 进行消息转换
ESQL 是一种功能强大的数据转换语言,可用于包括计算节点在内的许多 MQSI 转换节
点。ESQL 能够和用 SQL 方式执行常规数据库操作一样的方法在消息内操作结构化数据。
这样,就可以自由地在消息内容和数据库数据源之间交换数据,作为“计算”操作的组
成部分。
“根”元素是消息结构的最高级别元素。由于计算节点拥有输入和输出消息终端,因此
这些元素分别称为 InputRoot 和 OutputRoot。构成整个文档的元素被视为“根”元素的
子元素,并以如下方式引用:
InputRoot.element1
InputRoot.element2
下面是可放在队列上的 XML 消息的实例:
<transactionrecord>
<station>JUPITER</station>
<amount>1600</amount>
<transdate>20001-11-09 23:00:51.123000</transdate>
</transactionrecord>
在消息流中,当通过 MQInput 节点从队列检索消息,并将其传递到进行消息发送的计算
节点时,该消息具有以下形式结构:
Root
Properties
CreationTime=GMTTIMESTAMP '1999-11-24 13:1:'
(a GMT timestamp field)
..and other fields...
MQMD
PutDate=DATE '19991124'
(a date field)
PutTime=GMTTIME '131 '
(a GMTTIME field)
...and other fields...
MQRFH
mcd
msd='xml'
(a character string field)
..and other fields...
XML
transactionrecord
station=Jupiter
(a character string field)
109
amount=1600
(a character string field)
transdate=20001-11-09 23:00:51.123000
(a character string field)
我们可以从这里看到在消息内发现的多种子结构:
一般消息属性
MQSeries 消息描述符(MQMD)
MQSeries 集成器规则和格式化标题 (MQRFH2)
在此案例中,消息正文本身是 XML 文档
要引用输入消息内的 transactionrecord 数据元素,我们可使用 ESQL 表达式:
InputRoot.XML.transactionrecord
或
InputRoot.XML.(XML.tag)transactionrecord
后者显示一个实例,其中事务节点被完全限定为 XML 标记元素。尽管在上面的实例中,
并非必须使用此完全限定的批注,但把(XML.tag)限定符包含在内是很好的做法,因
为在带有 XML 文档标题的标准格式 XML 文档中,分析程序可能会将 XML 消息正文标
记的引用错误解释为 XML 文档标题属性的引用。
上面概述了 ESQL 中数据元素引用的基本形状。有关详细信息,请参见《MQSeries
Integrator ESQL 引用 2.0.2 版》编号: SC34-5923-00,其中的引用是相当全面的 ESQL
引用。该手册讲述了可用 ESQL 建立的全套有效构造,在此我们不再重复。相反,我们
将继续集中介绍建立实例所使用的技巧。
实例:把元素从输入复制到输出
作为实例,请参看这样一种情形,我们要使用输入 XML 消息和从数据库检索的信息创
建新的 XML 消息。表 7-1 显示要使用的输入和输出。
表 7-1 创建新的 XML 记录
输入: transactionrecord
110
来自数据库的输入
输出:logentry
站=Jupiter
站=Jupiter
金额=1600
金额=1600
输入: transactionrecord
来自数据库的输入
输出:logentry
transdate=20001-11-09
transdate=20001-11-09
23:00:51.123000
23:00:51.123000
余额=25000
新余额=25000
为此,我们使用以下代码摘录:
SET OutputRoot.XML."logentry"."transdate"= InputRoot.XML."transactionrecord"."transdate";
SET OutputRoot.XML."logentry"."amount"=
InputRoot.XML."transactionrecord"."amount";
SET OutputRoot.XML."logentry"."station"=
InputRoot.XML."transactionrecord"."station";
SET OutputRoot.XML."logentry"."newbalance"=
THE(SELECT ITEM T.BALANCE FROM Database.PDK.STATIONSFUNDING AS T WHERE T.NAME
=TRIM(InputRoot.XML."transactionrecord"."station"));
在这里,我们看到九个代码行上的四个 ESQL 代码语句。为增强易读性,语句可以按所
需的行数任意换行,但是,每个语句都必须以一个分号结尾。请注意,最后一个 SET
语句分布在三行上,表明语句到分号处才结束。
第一行把输入 “transactionrecord” XML 文档元素的 “transdate” 子元素复制到输出
“logentry” XML 文档元素的 “transdate” 子元素。
在这种分配操作中,引用的元素可能是叶元素(即它们本身不包含子元素),也可能是
包含任意数量子元素级别的子结构。无论适用于何种情况,分配语句都会把完整元素从
输入位置复制到输出位置,输出位置包含可能存在的任何子元素。后面的两个语句相似。
最后一个语句显示您如何使用数据库选择将信息添加到仍在消息流中使用的数据流。
实例:更为复杂的任务
下面请看此实例:
SET OutputRoot.XML."RATECALC"."LOG"=THE (SELECT L.MQMD_MSGID,
L.MQMD_CORRELID,L.MQMD_REPLYTOQ,L.MQMD_REPLYTOQMGR,L.MQMD_USERID,
L.MQMD_FORMAT,L.MQMD_ENCODING,L.MQMD_CODEDCHARSETID,L.MQMD_VERSION,
L.MQMD_FEEDBACK,L.MQMD_REPORT,L.MQMD_MSGTYPE
FROM Database.RATECALCMSGLOG AS L
WHERE L.MQMD_MSGID =CAST(InputRoot.MQMD.CorrelId AS CHAR));
在这里引入许多概念。
111
ESQL 可用来从结构化消息提取数据元素列表,其方法与使用 SQL 查询关系数据库(如
DB2 ) 的 方 法 相 似 。 在 上 面 的 实 例 中 , 选 择 查 询 的 目 标 是 复 合 数 据 元 素
“RATECALC”.”LOG”,该元素本身就是 OutputRoot 的 XML 根元素。
我们还可以看看如何使用 THE 谓词。包含此谓词表示查询仅产生一行,省略 THE 谓词
表示查询产生不只一行。因此,SET 语句的被分配者可能会具有多个实例。在此实例中,
被分配者应带有数组[]后缀,表明由于此操作可能会创建被分配者的一个以上的实例。
提示:在上面的每个实例中,通常的做法是把所有数据元素名称包含在“双引号”之内。
这种方法会避免在元素名称中使用特殊字符时可能会出现的任何潜在混乱。
如果在代码中分配文字串值,应在“单引号”内表示这些文字串值。
7.3.3
访问数据库:计算节点和数据库节点
在计算节点中可以包含 ESQL,以便选择、更新或删除数据库数据源中的条目。在消息
转换并非必需但却要求数据库操作的情况下,取代使用计算节点的比较好的选择就是使
用数据库节点。
MQSI 包含许多适合于各项关键 SQL 操作的专用数据库节点,即 DataInsert、DataUpdate
以及 DataDelete 节点。在使用可通过图形方式操作的消息数据时,这些节点非常有用,
除此之外,不会带来任何其他好处。
实例中,我们在所有数据库操作中都使用了一般数据库节点。图 7-3 显示的实例消息
流包含一个计算节点(访问数据库表)和一个数据库节点(如果满足“匹配”过滤器节
点中的条件,该数据库节点将删除数据库表中的条目)。
112
图 7-3 组成数据库通路的消息流实例
为了说明计算节点与数据库节点之间的区别,让我们仔细看一下图 7-3 中显示的“恢
复 MQMD 和映射到输出”(Restore MQMD and Map to Output)计算节点及“清除消息
日志”(清除消息日志)数据库节点。
成功获得后,“SOLARSYS.RATECALC.ACK”节点的输出节点被传递到“恢复 MQMD
和映射到输出”计算节点。这会导致执行此节点的逻辑。图 7-4 显示部分定制逻辑。
在第 271 页的“计算节点:恢复 MQMD 和映射到输出”中将详细讲述此节点。
113
图 7-4 恢复 MQMD 和输出计算节点图
注意“输入”窗格中的数据库。要引用 SELECT 语句中的数据库,这是必需的。SELECT
的结果会临时存储在 “RATECALC”.“LOG”XML 文件夹中,然后通过引用此结果来
生成 OutputRoot。
计算节点的 OutputRoot 将传递到检查必需条件的过滤器节点。如果条件得到满足(结果
为 true),消息就会传递到“清除数据库日志”(Clear Database Log)数据库节点(参
见图 7-5),并且从数据库表中删除指定的行。
114
图 7-5 清除消息日志
“清除消息日志”数据库节点只有一个功能,即执行数据库操作。在此案例中,执行的
数据库操作为 DELETE FROM。
7.3.4
过滤器节点
在消息流中,根据执行时存在的条件来控制流向非常有用。为此,我们使用过滤器节点。
在过滤器节点中,您可以使用 ESQL 建立一个得出“真”或“假”值的表达式。根据解
析表达式的结果,把控制传送到适用的输出终端。
再看看图 7-3 显示的消息流。您会注意到,对于“匹配”(Match)过滤器节点,有四
个可能的输出终端。过滤器节点上的可能输出为:“真”、“假”、“未知”或“失败”。
在图 7-6 中,我们可以看到此节点的属性。ESQL 表达式描述了来自作为该节点输入消
息的元素。测试该值看是否为“不为空”(NOT NULL)。如果此条件为真,则把控制
分别传送到“真”输出终端以及其他节点。
115
图 7-6 匹配过滤器节点属性
您可为过滤器节点添加更多输入以便添加数据库表,该数据表作为表达式中使用的数据
源。这样,您就可以使用传入的数据元素从数据库查找值,并可在表达式中测试这些值。
RouteToLabel 节点
根据消息目标列表的内容,您可以使用 RouteToLabel 节点提供动态路由。目标列表保存
一个或多个目标标签节点的条目,这些目标标签节点是使用其“标签名称”(Label
Name)的属性识别的,而不是使用节点名称进行识别。
标签
标签节点是 RouteToLabel 节点处理的消息的已命名目标。如上所述,标签节点是
RouteToLabel 节点处理消息时,由消息的目标列表中某个条目识别的。
可在《使用控制中心的 MQSeries Integrator 版本 2.0.2》编号:SC34-5602-03 中,找
到有关 RouteToLabel 节点和 Label 节点更为全面的解释和实例。.
116
7.3.5
MQOutput
MQOutput 节点通常是消息流的行尾,这些消息已进行了一些转换或执行了一些数据消
息发送,并已产生新的输出消息。参阅图 7-3,您就可以看到,如果没有发生错误,就
把转换的消息放在标有“RATECALC.ACK.TEST.OUT”的 MQOutput 节点上。可以在
MQOutput 节点的属性页中指派输出队列,也可以使用“目标列表”(Destination List)
动态指派属性列表。
7.3.6
MQReply
MQReply 节点除了把输出消息放在消息标题 ReplyToQ 指定的队列上之外,其他方面都
与 MQOutput 节点相似。这对于需要回应的请求来说非常理想。不能对此节点进行太多
配置,因为该节点完全依赖于消息标题 ReplyToQ 和 ReplyToQMgr 属性把消息放到输出
队列上。
7.3.7
IBM MQSI Aggregator插件
IBM MQSI Aggregator 插件——SupportPac IA72,将为我们提供应用程序执行分解/重
组功能时所需的节点。该插件提供若干工具以获取消息,并将其拆分(分解)为多条发
送到多个目标的消息。然后,该插件侦听响应,并将其收集(重组)为一个回复。聚集
器插件具有一些执行这些任务的独特节点。在以下的讲述中,我们把该插件所提供的功
能集称为“聚集器”。
IBM MQSI Aggregator 插件是受支持的 SupportPac。有关此节点的最新信息,请访问以
下网页:http://www-4.ibm.com/software/ts/mqseries/txppacs/ia72.html
第 118 页上的图 7-7 显示 SOLARSYS_STATEMENT_REQUEST 消息流,该消息流
使用聚集器接受请求,并把消息拆分成三个不同后端请求。然后,聚集器接收来自后端
应 用 程 序 的 回 复 , 把 它 们 关 联 为 一 个 回 复 , 并 根 据 原 始 输 入 消 息 ReplyToQ 和
ReplyToQMgr 属性返回给用户。
117
图 7-7 是用聚集器节点的 S_STATEMENT_REQUEST 消息流
SpAggregateCreate 和 SpAggregateReply 节点共同负责扇出消息和聚集回复。您必须至少
有一个 SpAggregateCreate 节点,并至多有一个 SpAggregateReply 节点。两个节点通过
其 AggregateName 属性进行关联。
请参阅图 7-7,让我们仔细观察具体工作原理。
在此消息流中,MQInput 节点监控队列 SOLARSYS.STATEMENT.REQUEST。成功获取
后 , 消 息 发 送 到 计 算 节 点 “ CorrelId = MessageId ” 。 此 节 点 只 用 来 确 保 存 在
MQMD.CorrelId , 这 是 聚 集 器 节 点 的 要 求 , 即 您 必 须 具 有 MQMD.CorrelId 。
MQMD.CorrelId 和 MQMD.MessageID 不必相等。
118
从“计算”(Compute)节点,请求拆分为三个单独的请求。需要将每个请求传送到
SpAggregateCreate 节点的输入终端。
每个 SpAggregateCreate 节点均有一个输入节点和两个输出节点。输入终端接收要处理
的消息。输出节点“输出”和“失败”是两个终端,消息传送到此并分别进行正常处理
和错误处理。
图 7-8 显示 SpAggregateCreate 节点的基本属性。除序列号之外,每个接受扇出消息的
SpAggregateCreate 节点的设置都将是相同的。稍后,序列号将由 SpAggregateReply 节点
来 区 分 每 个 消 息 的 响 应 。 AggregateName 用 于 将
SpAggregateCreate 节 点 与
SpAggregateReply 节点链接起来。
图 7-8 SpAggCreate1 节点
图 7-9 显示 SpAggregateReply 节点的基本属性,该节点将处理各个响应。AggregateName
与 SpAggregateCreate 节点相同,把对请求的回复与 SpAggregateReply 节点链接起来,
并将其重组为一个回复,然后返回给请求者。
图 7-9 SpAggregateReply1 基本属性
119
SpAggregateReply 节点具有一个输入终端和五个输出终端。其输出终端为:
Out
Failure
LateReplies
TimedOut
Catch
上面这些输出终端名称的意思非常明确。
可以把 SpAggregateCreate 节点的输出终端连接到计算节点,或连接到针对后端请求执
行特定转换的子消息流。
尽量不要更改 MQMD.MessageID 和 MQMD.CorrelId 的内容。SpAggregateCreate 节点已
经记录了这些属性,而且当回复从各个后端请求返回时,SpAggregateReply 节点都要求
原始的 MQMD.MessageID 在 MQMD.CorrelId 之中。这是用于请求/回复消息发送标准
的 MQSeries 编程。
另外,SpAggregateCreate 节点已更改了 RepyToQ 的内容,这样回复就返回上面显示的
SpAggregateReply 节点中指定的 ReplyToQueue。
如果由于历史系统的要求而无法使 MQMD 属性保持相同,您可以把消息标题的内容保
存到同一数据库。有关实例,请参阅后面第 262 页上的 14.3.3 小节:“MAP_RATECALC
_CICS_REQUEST 子消息流”。
图 7-10 显示 SpAggregateReply 节点的“高级”属性。
图 7-10 SpAggregateReply1 高级属性
120
在 gregatedReplyDestination 字段中,我们指定回复节点应使用哪种方法把回复消息放到
回复队列。使用 ReplyToQueue 方法,即此节点应使用 MQMD ReplyToQ 和 ReplyToQMgr
属性以确定回复消息目标。这些值将会被还原为从原始请求消息记录的原始值,然后用
来发送回复消息。MQMD.CorrelId 将通过原始请求消息的 MQMD.MessageID 进行设置,
这同样是标准的 MQSeries 编程方法。
我们没有指定 ReplyToQueue,而是指定 DestinationList 或 Default。选择 DestinationList
会导致把原始请求消息的 ReplyToQ 和 ReplyToQMgr 添加到聚集消息 DestinationList 中。
然后,可以由将 Destination 模式设置为 DestinationList 的 MQOutput 节点对此进行处理。
选择 Default 会导致建立不包含任何特殊细节的聚集消息,并假定开发者将把 Out 终端
连接到适当的输出队列。
AggregatedReplyDataType 允许您从 BLOB 或 XML 中进行选择。如果选择了 XML,返
回的回复消息需要是有效的 XML 格式。
AggregatedReplyLabelName 是字符串,它将用来构造 XML 回复的标记名称。使用的实
际标记名称将是 AggregatedReplyLabelName 与在 SpAggregateCreate 节点中指派的序列
号的组合。在我们的实例中,回复消息将会是一个 XML 流,各个回复都分别带有标记
名称“STATEMENT_REPLY10”、“STATEMENT_REPLY20”和“STATEMENT
_REPLY30”。这样可以非常容易地对 XML 回复进行分析。对于 BLOB 消息类型,您
可以使用 SeparatorString 或 HexSeparatorString 来区分要分析的回复。
Persistent 属性是控制聚合实例持久性的 Boolean 值。默认值为 false,即聚合数据将仅存
储在内存中,因此,如果代理程序失败,就无法恢复此消息流。设置为 true,聚集器就
会在 SpAggregateCreate 和 SpAggregateReply 节点中处理聚合消息时,把恢复消息写到
本地队列。如果将此值设置为 true,应确保消息流中的输入和输出消息操作都是事务处
理性的,因为会将恢复消息以事务处理方式编写为现有消息流的组成部分。
121
7.3.8
可复用消息流
MQSI 的一项主要功能是:能够把消息流存储为可复用组件,将这些组件链接在一起,
并用作大型操作性消息流(我们称之为“可应用”消息流)的组成部分。这样,就可以
自然条件及明确的输入、操作和输出规范,对完整操作中的各个组件进行开发和测试。
例如,有些转换可以要求以 ESQL 形式编写复杂代码。明显优势是只需执行此操作一次,
而且重要的是,MQSI 共享配置中只需有给定转换定义的一个实例。 一旦用此方法定义
复杂操作的组件,那么将子消息流串接在一起共同编译完整的商业操作就会变得非常简
单。
通过将 InputTerminal 和 OutputTerminal 基元用作输入和输出来源以定义消息流,可反复
把该消息流用作构成更大消息流组成部分的节点。因此,不能把消息流节点应用到代理
程序,因为消息流节点不以独立的方式工作。它们只能用作更大消息流的组成部分。
可以建立通用操作,作为可复用消息流组件,而这些组件可用作更大消息流的节点。图
7-11 显示一个实例,该实例说明如何使“RATECALC_CICS_REQUEST”消息流成
为可复用的消息流,从而使其逻辑可在更大消息流中重复使用。对于通用数据库更新、
查找以及商业代理系统中的其他通用方法来说,这可能是一项强大的功能。
图 7-11 可复用的消息流:MAP_RATECALC_CICS_REQUEST
122
输入和输出是通过使用终端节点类型 InputTerminal 和 OutputTerminal 定义的。而消息流
的其余部分则是用与可应用消息流同样的方式定义的。从该语句,您应推断出不可以孤
立应用组件消息流,而只能把它们作为可应用消息流的组成部分。
在保存此消息流时,可以将它拖动到另一个消息流,在那里,它将显示为消息流节点。
在第 118 页上的图 7-7 中,
您会看到这样的实例。在此实例中,名为“MAP_RATECALC
_CICS_REQ”的节点是上面定义过的“MAP_RATECACL_CICS_REQUEST”组
件消息流的实例。
很明显,使用消息流节点需根据其规范准备其输入。在“MAP_RATECACL_CICS_
REQUEST”流的情况下,该消息流节点需要具有“视图-语句-命令”类型的 XML 文
档,该文档的内容将匹配已由用户界面组件准备好的文档。
在消息流节点中使用属性升级
将常用操作保存为消息流节点,是一种实现重复利用 MQSI 代码的有用方法。对此功能
的一项扩展是,通过属性升级实现消息流节点可配置性的能力。属性升级使消息流用户
可为属于消息流级别的节点的已升级属性设置各种值。
例如,图 7-11 显示可复用消息流“MAP_RATECALC_CICS_REQUEST”。我们可
能需要升级“保存消息标题”(Save Message Header)节点的“数据源”(Data Source)
属性,因此,在测试消息流的同时,我们可以使 Data Source 属性指向与生产数据库相
反的数据库的测试版本。
如果不升级此属性,您就得把消息流内所有单个节点的 Data Source 名称设置为引用不
同的 Data Source。然后,当您把此节点应用到生产中时,就必须更改 Data Source 名称
的每个引用。
升级 Data Source 属性使您可以将所有单个节点的数据源设置为 PRODDATA,并可在测
试时把已升级数据源的值设置为 MYTESTDATA。已升级属性始终优先于相关节点内属
性的设置。
有关详细信息,请参阅《使用控制中心的 MQSeries Integrator 版本 2.0.2》编号:SC34
-5602-03,其中,详细讲述如何升级消息流节点属性。
123
7.3.9
测试消息流组件
通过将完整的消息流创建为一组更小、更简单的组件,测试过程成为一项不太艰巨的工
作。利用如图 7-12 所示的测试导线,可以单独测试每个子消息流。通过把消息流节点
连接成这样一根导线,您可以使用简单的 MQSeries 应用程序把具有所需格式的消息放
到输入队列,并可以使用获取消息的简单应用程序监控其结果。
只要设计具有一个以上输或输出终端的消息流组件,就会需要这种方法的一个变体(修
改的方法)。
图 7-12 测试导线实例
124
至于测试,我们创建了简单的消息流,通过简单改编提供有 MQSeries 的 MQSample 应
用程序,达到了把消息放到队列以及从队列获取消息的目的。 在需要数据库操作的地
方,我们使用 DB2 控制中心对数据库内容进行查询。
7.4 使用队列别名
要想在设计中具有灵活性,好的做法是使应用程序代码独立于其基本结构。在应用程序
代码内,使用特定队列管理器和队列名称会不可避免地造成将来维护费用的过高。
避免该缺陷的一种方法是,在用于队列管理器以及与其进行交互的队列的应用程序代码
中使用别名,把解释别名的工作留给支持性基础结构 MQSeries。可能还有其他更可取
的方法。例如:使用属性文件、ini 文件或某个数据库中存储的信息。 要使用数据库信
息,请在属性文件中存储刚好够用的信息连接数据库,然后在数据库表中查找队列排队
连接参数。此技术可用于针对您所连接的环境实例(开发、测试、质量保证或生产)的
许多应用程序变量。
当把 JMS 用作 API 时,一个选项是使用 JMS 配置详细信息来处理应用程序对象名称和
真实对象引用之间的转换。在已使用 MQSeries 类(用于 Java)的地方,必须使用一组
使消息发送功能能够指向适用队列的适用队列和队列管理器别名,来委托队列管理器。
您可以针对各种环境使用不同的 JMS 配置存储库,这样,对象名称就可始终保持相同。
队列排队定义下面的对象名称具体取决于您要与 JMS 名称服务器环境中的哪个实例进
行交互。
另一个 API——应用程序消息发送接口(AMI)——是基于策略的编程接口。利用 AMI,
可根据某种策略将消息发送到服务,而 AMI 将把消息转换为队列名称、队列管理器名
称以及其他 MQSeries 选项。这样,就没有必要使程序员了解您的 MQSeries 网络的实施
细节。
125
126
8
系统设计和管理
系统管理不仅是应用程序设计的重要阶段,而且是企业日常操作活动中的因素之一。系
统管理涉及许多领域,一般包含:
应用程序管理
性能监控
可用性管理
安全管理
灾难恢复
操作系统和网络管理
资产管理
软件分布
问题报告
更改管理
上述众多方面均为一般考虑事项,其涉及企业操作活动的方方面面。本章暂不讲述这些
主题,而是主要考察针对 MQSeries 和 MQSeries Integrator 的系统管理技术。
有关 WebSphere 高级版版本 4.0 的系统管理原则,请参见《使用 WebSphere 应用程序服
务器的自助服务模式》版本 4.0,编号:SG24-6175。
127
8.1 系统设计
MQSI 不仅是高度可扩展的而且是高度分布式的操作环境。因此,要有效实施 MQSI,
需要的不仅仅是良好的消息流设计案例。如何构建 MQSeries 基础结构及 MQSI 组件的
分布,都将在确定应用程序如何执行方面发挥一定的作用。
良好的系统管理始于良好的系统设计。设计 MQSeries 和 MQSI 拓扑时,应考虑以下几
个主题:
MQSeries 队列管理器角色和关系
MQSeries 网络中的 MQSI 服务和代理程序
支持网络内 MQSI 数据库的布置
8.2 MQSeries设计
MQSeries 系统管理的复杂程度正好与所管理的 MQSeries 网络的大小成正比。虽然最简
单的 MQSeries 网络只由单个 MQSeries 队列管理器及其资源组成,但是也可以大到包含
分布在整个企业的成百上千个队列管理器。
若增加其复杂程度,比如,增加 MQSeries 的中间件功能,MQSeries 网络就可跨越许多
包含不同网络、硬件、操作系统以及应用程序的平台。每个平台都有其独特的具体系统
管理需要和要求,更不用说 MQSeries 管理工具和界面还能支持不同的级别。
现今,市场上 MQSeries 系统管理工具不断增加并不令人称奇,而且其会从方法、形状
或形式上设法提供以下 MQSeries 系统管理任务的一种或多种组合:
配置管理——能够应用 MQSeries 代码以及从单一控制点创建和删除包括队列管理
器、队列、通道和过程在内的各种 MQSeries 对象。
操作管理——能够从单个点启动和停止诸如队列管理器、通道、触发器监控器、
通道侦听器以及启动
器这样的资源。
问题管理——能够从单一控制点检测、跟踪和解决 MQSeries 对象出现的问题。
128
性能管理——能够从单一控制点确定 MQSeries 对象。
本书的重点不在于介绍现今市场上不断增多的系统管理工具,而是着重向读者介绍
MQSeries 的基本系统管理原则,以及简要介绍开箱即可使用的功能和工具。可以说,
如果 MQSeries 系统管理需要的工具超出本产品所提供的工具范围,则所选系统管理工
具应该是对企业范围内系统管理框架的扩展,或者至少本产品非常适应该框架的需要。
下面的内容将讨论 MQSeries 系统管理:
管理 MQSeries:NASG 的 MQSeries 专家 Les Yeamans 对 MQSeries 系统管理市场
进行了调查,并对现有的主要产品进行了独立评估。有关详细信息,请访问:http:
/ / www.software.ibm.com / ts / mqseries / library / independent / nasg /
vendor.html
MQSeries SupportPac MS0D : 选 择 MQSeries 系 统 管 理 工 具 , 这 是 一 个 三 类
SupportPac,请访问:http://www.software.ibm.com/ts/mqseries/txppacs
MessageQ.Com: 请访问:http://www.messageq.com
本章所涉及的 MQSeries 版本为 IBM MQSeries 版本 5.2。文中提及的 Windows 指的是
Windows NT 或 Windows 2000。
8.3 MQSeries队列管理器设计
队列管理器是 MQSeries 网络的中心,它向各个应用程序提供消息发送服务。应认真考
虑如何定义队列管理器,以及其在 MQSeries 和 MQSI 中所扮演的角色与其之间的相互
关系。
8.3.1
队列管理器角色
当设计用以支持应用程序路由器的队列管理器网络时,我们必须考虑到期望队列管理器
履行的不同角色,以及我们可以拥有的应用程序设计优先权。这样,就能够定义用于把
角色实例应用到完整网络的模板,从而可以更容易地管理扩展能力。
129
有些队列管理器可能需要支持关键的 MQSI 服务,如“配置管理器”、“用户名服务器”
或“代理程序”。其他队列管理器则专门用来直接服务于来自应用程序的程序表示层
(如 WebSphere Application Server 上的 EJB 容器)的呼叫。
服务于代理程序的队列管理器需要主持本地队列,这些本地队列用作该代理程序的消息
流的输入源。应用程序队列管理器具有相似的要求。同时,我们还必须考虑到在把角色
的新实例定义到网络时所必需的通道定义。
实际上,出于性能或成本方面的考虑,最好在单个队列管理器中应用一个以上的角色。
这当然是可能的,如果在设计中重视角色,那么您在应用工作时就有极大灵活性。
应用程序也可能会有特定的设计要求。例如,在 EJB 与消息代理程序之间实现最大消息
吞吐量性能可能至关重要。由于性能受网络连接问题的影响,因而在同一个队列管理器
上主持角色通常可以改进性能。
或者,性能可能并不是最重要的,但必须保证给定消息只提供一次。在使用单独队列管
理器主持应用程序处理器和角色的地方,才能实现有保证的消息发送。
8.3.2
群集的应用
为了便于管理,可以在群集中配置队列管理器。若建立队列管理器群集网络,应配置一
对队列管理器作为群集存储库队列管理器。这些队列管理器具有一个特殊任务,因为它
们保存群集内队列管理器和共享对象的完整记录。可以添加更多队列管理器作为群集成
员,并切有到达其中一个存储库队列管理器的已定义群集发送者通道。
群集的管理优点
在使用分布式队列排队的传统 MQSeries 网络中,每个队列管理器都是独立的。如果一
个队列管理器需要把消息发送到另外一个队列管理器,该队列管理器必须已经定义了传
送队列、到达远程队列管理器的通道,以及该队列管理器将把消息发送至每个队列的远
程队列定义。
130
每个群集是由一组队列管理器组成的,可将这些群集管理器建立为相互之间直接通过单
个网络进行通信,而无需复杂的传输队列、通道以及队列定义。
可以很容易地建立群集,其中通常包含逻辑上以某种方式相关联并且需要共享数据或应
用程序的队列管理器。一旦创建了群集,群集内的队列管理器就可以相互通信,而且无
需复杂的通道或远程队列定义。即使是最小的群集都会减少系统管理费用。
在群集内建立群集管理器网络需要的定义,比建立传统的分布式队列排队环境要少。减
少需要进行的定义之后,您就可以更加方便快捷地建立或更改网络,而且还可减少在定
义中出错的风险。
群集详细信息
要建立群集,您需要为每个队列管理器定义一个群集发送者(CLUSSDR)定义和一个
群集接收者 (CLUSRCVR)定义。而无需定义任何传输或远程队列。
在群集环境中,您应把一个(两个最好)队列管理器提升为完全存储库队列管理器。也
就是说,像这样的队列管理器了解群集中的所有其他队列管理器。该队列管理器知道什
么群集对象(本地队列或任何其他类型的对象)是由哪个队列管理器主持,并且知道如
何与这些队列管理器进行联系。其中后面特性的意思是,完全队列管理器是具有发送者
和接收者通道定义的模板定义,该模板定义可用来根据需要自动创建新的发送者/接收
者通道。
要使两个队列管理器 QM1 和 QM2 成为名为 MY_CLUSTER 的群集的完全存储库队列
管理器,需要执行以下 MQSeries 命令:
在 QM1 上:
DEFINE CLUSCHL(TO.QM1)CHLTYPE(CLUSRCVR)+
CONNAME(hostname1)+
CLUSTER(MY_CLUSTER)
DEFINE CLUSCHL(TO.QM2)CHLTYPE(CLUSSDR)+
CONNAME(hostname2)+
CLUSTER(MY_CLUSTER)
ALTER QMGR REPOS(MY_CLUSTER)
131
在 QM2 上:
DEFINE CLUSCHL(TO.QM2)CHLTYPE(CLUSRCVR)+
CONNAME(hostname2)+
TRPTYPE(TCP)+
CLUSTER(MY_CLUSTER)
DEFINE CLUSCHL(TO.QM1)CHLTYPE(CLUSSDR)+
CONNAME(hostname1)+
TRPTYPE(TCP)+
CLUSTER(MY_CLUSTER)
ALTER QMGR REPOS(MY_CLUSTER)
类型 CLUSRCVR(或群集接收者)的对象指定队列管理器需要其他队列管理器如何与
其对话,或从技术上说,其他队列管理器应如何创建发送者通道来把消息发送到此队列
管理器。
类型 CLUSSDR(或群集发送者)的对象应向队列管理器提供与群集中其他完全存储库
队列管理器进行通信的发送者通道。
ALTER QMGR 命令最终使该队列管理器成为完全存储库队列管理器。
这些定义到位后,您就可以在 QM1 和 QM2 之间进行双向通信。把本地队列添加到 QM1
中并指定群集名称 MY_CLUSTER 时,QM1 就会把该对象的定义传递到第二个完全存
储库队列管理器 QM2。
DEFINE QLOCAL(CLUSTERED_QUEUE)CLUSTER(MY_CLUSTER)
既然 QM2 有了到达 QM1 的通道,而且 QM2 现在知道 CLUSTERED_QUEUE 存在于
QM1,就不需要定义远程队列对象!请注意,迄今为止,我们尚未定义传送队列。MQSeries
群集通道使用通用预定义传送队列,称为 SYSTEM.CLUSTER.TRANSMIT.QUEUE。
向群集中添加附加队列管理器时,MQSeries 群集的实际优点非常明显。要把 QM3 添加
到群集,需要定义群集接收者通道,以便向群集表明其他队列管理器应如何与 QM3 对
话:
DEFINE CLUSCHL(TO.QM3)CHLTYPE(CLUSRCVR)+
CONNAME(hostname3)+
TRPTYPE(TCP)+
CLUSTER(MY_CLUSTER)
132
您需要对群集的 QM3 部分做的下一件也是最后一件事情是,创建到达完全存储库队列
管理器的群集发送者通道。因为有两个完全存储库队列管理器可供您选择,所以选择哪
一个并不重要。
DEFINE CLUSCHL(TO.QM2)CHLTYPE(CLUSSDR)+
CONNAME(hostname2)+
TRPTYPE(TCP)+
CLUSTER(MY_CLUSTER)
创建此对象时,QM3 会启动通道 TO.QM2,并赋予 QM2 通道 TO.QM3 的定义。QM2
会立即使用该定义创建从 QM2 到 QM3 的发送者通道。此后,QM3 就可以使用群集 MY
_CLUSTER 中的任何对象了!当某个应用程序连接到 QM3,并打开队列 CLUSTERED
_QUEUE 时,QM3 就不会知道该对象在哪里。QM3 至少知道其本身并不主持此对象。
因此,QM3 会询问其完全存储库队列管理器 QM2 有关此对象的情况。QM2 用以下定
义进行回复:CLUSTERED_QUEUE 是 QM1 主持的一个本地队列。由于 QM3 不知道
如何与 QM1 对话,QM3 向 QM2 发送另一个请求,以便获取 QM1 的通信参数。现在,
QM2 通过群集接收者定义进行回复, QM3 使用此定义自动创建从 QM3 到 QM1 的发
送者通道。此时,QM3 无需任何手动定义就能够把消息发送到 QM1。如果使用 MQSeries
Explorer GUI 界面,定义群集甚至更为容易。
工作负载平衡和接管
群集的另一个巨大优势是可以进行工作负载平衡和接管。假定我们有在 QM1 和 QM2
上主持的本地队列 WORKLOAD。该队列定义为群集。
当某个应用程序连接到 QM3 并打开队列 WORKLOAD 时,QM3 就能选择将把消息发
送到哪个队列管理器。随后,QM3 将选择它能够与之建立连接的队列管理器。如果通
道 TO.QM1 已进入重试,而且 TO.QM2 在运行,QM3 就会选择把消息发送到 QM2。如
果两个通道都在活动,而且应用程序或管理员允许,QM3 就会以循环方式把消息发送
到两个队列管理器。
MQOPEN 现在有控制工作负载分散的新选项。如果您要在 MQOPEN 时间选择目标,就
请指定选项 MQOO_BIND_AT_OPEN。如果要把工作负载分散到活动系统上,您需
要使用选项 MQOO_BIND_NOT_FIXED。使用其中任何选项,应用程序都可以控制
是否把 MQOPEN 和 MQCLOSE 之间生成的所有消息发送到一个系统或发送到主持目标
队列的每个系统。
133
尽管上面对于 MQSeries 群集的概述并不全面,但我们还是希望您对此功能已获得一些
基本了解,并知道此功能如何可以帮助 MQSeries 管理员通过较少定义创建更为强大和
可靠的 MQSeries 网络。
8.4 MQSeries管理工作的管理
MQSeries 管理任务包括创建、开始、变更、查看、停止和删除 MQSeries 对象,其中包
括:
MQSeries 队列管理器
MQSeries 队列
过程定义
通道
群集
名单
每个 MQSeries 网络都有队列管理器的一个或多个实例,相互连接的队列管理器的整个
网络能通过名称了解该队列管理器。对于所有其他对象类型而言,每个对象都有一个与
之关联的名称,并可通过该名称进行引用。在同一队列管理器和对象类型内,这些名称
必须是唯一的。例如,可以有一个队列以及一个具有相同名称的进程,但不能有两个具
有相同名称的队列。
8.4.1
管理界面
这些 MQSeries 对象通过 MQSeries 管理任务进行管理,而这些 MQSeries 管理任务是通
过使用以下任意 MQSeries 界面执行的:
控制命令
MQSeries 命令(MQSC)
可编程命令格式 (PCF)
MQSeries 管理界面 (MQAI)
MQSeries Explorer(仅适用于 Windows)
MQSeries Services 管理单元(仅适用于 Windows)
Web Administration(仅适用于 Windows)
134
控制命令
控制命令用来在队列管理器、命令服务器和通道上执行操作。控制命令可分为三个类别,
如表 8-1 所示。
表 8-1 控制命令类别
类别
说明
队列管理器命令
队列管理器控制命令包括用于创建、开始、停止和删除队
列管理器和命令服务器的命令。
通道命令
通道命令包括用于开始和结束通道和通道启动器的命令。
工具命令
工具命令包括与以下命令关联的命令:
-
运行 MQSC 命令
-
转换退出
-
权限管理
-
记录恢复队列管理器资源的媒体图像
-
显示和解析事务
-
触发器监控器
-
显示 MQSeries 对象的文件名
MQSC 命令
MQSC 命令用来在队列管理器对象上执行操作。这些命令是使用 runmqsc 命令发出的。
这既可以从键盘以交互方式完成,也可以通过重定向标准输入设备(stdin)并从 ASCII
文本文件运行一系列命令来完成。无论采用哪种方法,命令的格式都是相同的。
PCF 和 MQSeries 管理界面(MQAI)
MQSeries 可编程命令格式(PCF)命令的目的是把管理任务编成管理程序。这样,您就
可以从程序创建队列和过程定义,以及更改队列管理器。
PCF 命令包含由 MQSC 工具所提供的同样的系列功能。每个 PCF 命令都是一个嵌入
MQSeries 消息应用程序数据部分的数据结构。使用 MQI 功能和 MQPUT,将每个命令
发送到目标队列管理器,其方式与任何其他消息相同。接收消息的队列管理器上的命令
服务器将发送来的每个命令解释为命令消息,并运行该命令。为了收到回复,应用程序
会发出 MQGET 呼叫,回复数据将以另一种数据结构返回。然后,应用程序就可以处理
回复,同时并做出相应的反应。
135
提示:与 MQSC 命令不同的是,PCF 命令及其回复的格式使用的是无法阅读的文本格
式。
您可以使用 MQAI 更容易地以编程方式对操作困难的 PCF 消息进行访问。
MQSeries Explorer
MQSeries for Windows 提供了一种称为 MQSeries Explorer 的管理界面,该管理界面会取
代使用控制命令或 MQSC 命令来执行管理任务。
MQSeries Explorer 使您只需简单地将 MQSeries Explorer 指向队列管理器和所感兴趣的
群集,即可从运行 Windows 的计算机对您的网络进行远程管理。
第 139 页的 8.4.2“远程管理”概述了可使用 MQSeries Explorer 来管理 MQSeries 的平台
和级别,以及为使 MQSeries Explorer 能够管理远程 MQSeries 队列管理器而必须在它们
上面执行的配置步骤。
136
图 8-1 MQSeries Explorer
利用 MQSeries Explorer,您可以:
启动和停止队列管理器(仅在本地机上);
定义、显示和更改 MQSeries 对象(如队列和通道)的定义;
浏览队列上的消息;
启动和停止通道;
查看关于某个通道的状态信息;
查看群集中的队列管理器;
使用“新建群集”向导创建新的队列管理器群集;
使用“将队列管理器添加到群集”向导把对列管理器添加到群集;
使用“联接群集”向导将现有队列管理器添加到群集。
MQSeries Services 管理单元
MQSeries Services 管理单元可用来管理本地或远程 MQSeries for Windows 服务器。它还
允许您监控本地系统中的问题创建的警报。
137
图 8-2 MQSeries Services 管理单元
利用 MQSeries Services 管理单元,您可以:
启动或停止队列管理器;
启动或停止命令服务器、通道启动器、触发器监控器以及侦听器;
创建和删除队列管理器、命令服务器、通道启动器、触发器监控器以及侦听器;
把所有服务设置为在系统启动期间自动启动或手动启动;
修改队列管理器属性。此功能取代使用配置(mqs.ini 和 qm.ini)文件中的 stanzas;
更改默认队列管理器;
修改所有服务的参数,如 TCP 端口号或通道启动器队列名称;
如果某个服务失败,就修改 MQSeries 的行为,例如,重试启动该服务 x 次;
启动或停止服务跟踪;
启动或停止 MQSeries Web Administration。
Web Administration
MQSeries 提供了基于 Web 的应用程序,该应用程序使您可以从 Windows 工作站管理
MQSeries 网络中的所有系统。Web Administration 能够让您通过使用 Web 浏览器来执行
runmqsc 命令可以进行的一切操作。
138
另外,Web Administration 使您还能够构造更为复杂的脚本,其中包括条件逻辑、循环、
嵌套等。Web Administration 还包括一种简单的文件管理功能,您可以利用这种功能在
通用和专用数据存储区组织脚本文件。
有关执行这些功能的详细信息,请参阅 MQSeries Web 管理的联机帮助。
图 8-3 Wed Administration on Windows
主持这些新功能的 Web 服务器只能运行于 Windows,但提供人机界面的浏览器可以运
行于支持 Java 语言 Web 浏览器(如 Netscape Navigator 或 Microsoft Internet Explorer)
的任何平台。
8.4.2
远程管理
MQSeries Explorer、Web Administration 以及 MQSeries Services 管理单元都提供某种形
式的远程管理。
139
MQSeries Explorer 可以在以下平台上远程管理 MQSeries:
表 8-2 远程管理
平台
最低命令级别
AIX、UNIX
221
OS/400
320
OS/2 和 Windows
201
VMS 和 Tandem
221
MQ/390
请 参 阅 第 141 页 的 “ 使 用 MQSeries
Explorer 的注意事项”。
表 8-2 的平台和命令级别标题是指平台和命令级别队列管理器属性。必须确定它们支
持哪些系统控制命令。您可以参阅 MQSeries Explorer 窗口中的平台和命令级别属性,
如第 137 页的图 8-1 所示。
需要注意的是,MQSeries Explorer 和 Web Administration 界面都只能传输经由 runmqsc
或相当于平台输入的命令。也就是说,您不能使用它们来创建或删除队列管理器。虽然
MQSeries Services 管理单元允许启动和停止远程队列管理器及其关联过程,但是只有远
程系统在 Windows 上运行时才有效。
提示:可以在非 Windows 平台上执行数量有限的队列管理器远程操作,例如,启动和
停止通道,但 MQSeries Explorer、MQSeries Services 或 Web Administration 无法实现在
异构型多平台配置情况下的所有操作。
要从任何 MQSeries 管理界面对队列管理器进行远程管理,都需要具备以下条件:
用于受管理的队列管理器的命令服务器;
用于每个远程队列管理器的适当 TCP/IP 侦听器。这可以是 MQSeries 侦听器,也
可以是适当 INETD 守护程序;
每个远程队列管理器都需要称为 SYSTEM.ADMIN.SVRCONN 的服务器来连接通
道。此通道对于每个受管理的远程队列管理器都是必需的。
140
8.4.3
管理界面原则
决定管理界面或技术是否适合某种特定的操作,主要取决于平台类型和手头的 MQSeries
任务。
在 Windows 上,您可以使用 MQSeries Explorer 和 MQSeries Services 管理单元工具执行
多数常见管理和操作任务。这些工具使 Windows 成为非常方便的实验和开发环境。但
是,一旦创建了队列管理器,在使用其中一种脚本技术填充和操作队列管理器时就会更
加高效。
如果网络配置中包括安装 MQSeries 版本 5.1 的 Windows 服务器,就可以组合使用
MQSeries Web Administration 和 MQSeries Explorer,从而对非 Windows 队列管理器进行
有限的远程管理工作。否则,必须交互地使用 runmqsc 功能或通过脚本文件进行管理,
这与在以前版本 MQSeries 中的做法相同。
包含控制命令和 MQSC 命令的 MQSeries 脚本文件是一种很常用的管理 MQSeries 的方
法。可以把整个 MQSeries 配置存储在这样的脚本文件中,在网络中添加新的 MQSeries
节点之后,只需稍加改动,便可去定义新的队列管理器。
Web Administration 能够让您通过使用 Web 浏览器来执行 runmqsc 命令可以进行的一切
操作。它能够使您建立更加复杂的脚本,这样的脚本包括条件逻辑、循环、嵌套等。
Web Administration 还包括简单的文件管理功能,可用来在通用和专用数据存储区中组
织脚本文件。
使用 MQSeries Explorer 的注意事项
当在安装中决定是否使用 MQSeries Explorer 时,应牢记以下几点:
队列管理器越小,MQSeries Explorer 工作情况越佳。如果一个队列管理器上有许
多对象,而 MQSeries Explorer 又在视图中展开要显示的必需信息,您就会感到非
常慢。为了帮助您掌握何种情况下才算“许多”,我们举例说明:假定队列管理
器上有 200 多个或 100 多个通道,就需要考虑使用第三方企业控制台产品,而不
是 MQSeries Explorer。
MQSeries 群集最多可以包含数百个甚或数千个队列管理器。由于 MQSeries
Explorer 使用树形结构在群集中显示队列管理器,对于大型群集来说,就显得非常
庞大。群集的物理大小对于 MQSeries Explorer 的速度影响并不大,因为在选定队
141
列管理器之前,该 Explorer 没有连接到这些队列管理器。
消息浏览器在队列上显示前 200 条消息。但在屏幕上仅格式化和显示消息中包含
的前 1000 字节的消息数据,而不会完整地显示 1000 字节以上消息数据的消息。
MQSeries Explorer 无法管理这样的群集:其存储库队列管理器位于 OS/390 的
MQSeries。要避免此问题,请在 MQSeries Explorer 可以管理的系统上指定附加存
储库队列管理器。通过用此新的存储库队列管理器连接该群集,您就可以管理该
群集中的队列管理器,但这还取决于 MQSeries Explorer 的通常限制,比如所支持
的 MQSeries 的级别。
使用 Web Administration 的注意事项
当在安装中决定是否使用 MQSeries Web Administration 时,应牢记以下几点:
MQSeries Web Administration Web 服务器需要专用的 IP 端口号。
可以从互联网访问 MQSeries Web Administration——如果网络配置允许这样做的
话。
MQSeries Web Administration 的所有用户都需要在服务器计算机上有活动的
Windows 用户 ID,并且具有运行 MQSC 命令的充足用户权限。
要通过 Web Administration 管理远程队列,必须在系统之间配置串联消息通道或群
集。
8.5 MQSeries监控
您可以使用 MQSeries 检测事件监控队列管理器的操作。本节将简要介绍检测事件,并
详细介绍两个 Windows 服务管理单元。这两个管理单元使用 MQSeries 检测功能向用户
提供 GUI 事件和监控工具。
8.5.1
检测事件
无论何时,当检测事件在队列管理器检测到一组预定义条件时,均会生成若干特殊消息,
称为“事件消息”。
例如,以下条件会产生“队列已满”(Queue Full)事件:
队列已满事件是为指定队列启用的。
142
某个应用程序会发出 MQPUT 呼叫,以便把消息放到该队列。但是呼叫失败,因
为队列已满。
其他条件也可产生检测事件,包括:
到达队列的消息数目的预定义限制
在指定时间范围内不向其提供服务的队列
正在启动或停止的通道实例
在 MQSeries for UNIX 系统中,试图打开队列并指定未经授权用户身份的应用程序
如果把事件队列定义为远程队列,就可以把所有事件队列放在一个队列管理器上(针对
支持检测事件的那些节点)。然后,您可用生成的事件从单个节点监控队列管理器网络。
MQSeries 事件分类如下:
队列管理器事件
这些事件与队列管理器内的资源定义相关。
性能事件
这些事件是通知事件,告知某项资源已达到阈条件。
通道事件
通道在操作过程中检测到条件时报告的事件。
事件发生时,队列管理器把事件消息放在适当的事件队列上(如果已定义这样的队列)。
事件消息包含有关该事件的信息,您可通过编写适当的 MQI 应用程序来检索该信息。
每个事件类别都有其自己的事件队列。每个类别中的所有事件都会导致把事件消息放到
同一队列上,如表 8-3 所示。
表 8-3 事件队列
此事件队列……
包含消息,来自:
SYSTEM.ADMIN.QMGR.EVENT
队列管理器事件
SYSTEM.ADMIN.PERFM.EVENT
性能事件
SYSTEM.ADMIN.CHANNEL.EVENT
通道事件
143
8.5.2
MQSeries警报监控器(适用于Windows)
MQSeries 警报监控器是一种错误检测工具,它识别和记录本地设备上出现的关于
MQSeries 的问题。MQSeries 警报监控器显示 MQSeries 服务器本地安装的当前状态信息。
凭借 MQSeries 警报监控器,您可以:
直接访问 MQSeries Services 管理单元
查看所有有关未处理警报的信息
在本地设备上关闭 IBM MQSeries 服务
把警报消息通过网络路由到可配置的用户帐户,或路由到 Windows 工作站及服务
器
如果任务栏图标表明警报已出现,请双击该图标,打开警报监控器显示窗口。此窗口将
显示包含所有当前尚未处理警报的树型视图,并且按队列管理器分组。展开该树的节点,
查看有关于哪些服务的警报,并查看有关服务的以下信息:
服务的最新警报的日期和时间
失败的命令行
描述服务为何失败的错误消息
8.5.3
性能监控器(适用于Windows)
性能监控器是 Windows 的标准组件。它使您可以选择和显示有关 Windows 环境性能的
多种数据,其形式为表格式报告或图表。使用性能监控器,您可以监控 MQSeries 队列
上的消息深度以及消息到达及删除的速度。
要访问性能监控器,可依次单击“开始”->“程序”->“设置”->“控制面板”->
“管理工具”->“性能”。
首次启动性能监控器时,显示窗口为空。要添加所需资源,请在图表窗格上单击+号以
添加计数器(如图 8-4 所示)。
144
图 8-4 性能监控器
在“添加计数器”窗口(图 8-5)中,请选择要监控的项目。
图 8-5 添加计数器窗口
从性能对象下拉列表中选择“MQSeries 队列”。
选择要监控的项目:
145
-
当前队列深度,即队列中有多少条消息。
-
以最大队列深度百分比表示的队列深度,即队列有多满。
-
每秒钟消息入对率,即放入队列中的消息数目。它不一定是 MQPUT 的数目;
每个消息段算作一条消息。
-
每秒钟消息离队率,即从队列中删除的消息数目。
然后,从实例列表中选择队列。实例列表只包含在性能监控器启动之前就拥有插
入或删除消息的队列。
为每个选定的计数器单击“添加”。
完成后,单击“关闭”。
这样就显示出图表。使用菜单栏更新图表属性,从而控制图表详细信息。
图 8-6 队列深度图表实例
146
8.6 MQSeries重新启动及恢复
MQSeries 确保不会由于维护队列管理器活动的记录(日志)而丢失消息,这些队列管
理器处理消息的接收、传送和交付。MQSeries 使用这些日志进行三种类型的恢复:
按计划停止 MQSeries 时,进行重新启动恢复。
当意外故障停止 MQSeries 时,进行崩溃恢复。
进行媒体恢复,以恢复损坏的对象。
无论哪种情况,恢复功能都会把队列管理器恢复到其停止之前(回滚传输途中的事务除
外)所处的状态,并从队列中删除停止队列管理器时没有提交的所有消息。恢复功能恢
复所有永久消息,但在恢复过程中丢弃非永久性消息。
MQSeries 支持两种类型的日志记录:
循环日志记录
线性日志记录
每种日志记录类型都将记录的数据存储在一组文件中。两种类型日志记录之间的差别是
内容以及将文件链接的方式。
通过循环日志记录,把日志文件集有效地链接,从而形成一个环。收集数据时,按顺序
将其写入各个文件,这样就可以重复利用环中的日志文件。您可使用循环日志记录进行
崩溃恢复和重新启动恢复。
利用线性日志记录,把日志作为一系列连续文件进行维护。收集数据时,按顺序将其写
入各个文件,但不能重复利用文件中的空间,这样始终可以检索创建队列管理器以来的
任何记录。
由于磁盘空间是有限的,因而可能必须计划某种形式的存档。此外,如果是处理大量永
久性消息,则最终会充满所有日志文件。这会导致把操作员消息写到错误日志文件中,
而且系统管理员需要执行某些操作来释放更多可用日志空间,或重复利用现有空间。您
可以使用线性日志记录进行上述三种类型的恢复。
147
8.6.1
管理日志文件
如果使用的是循环日志,请确保有充足的空间保存日志文件。您可以在配置系统时进行
此项工作。日志所用磁盘空间的大小不会超过配置的大小,包括根据需要而创建的次要
文件的空间。
如果使用的是线性日志,记录数据时,会不断添加日志文件,而且使用的磁盘空间会随
时间推移而增加。如果记录的数据传输速率很高,新日志文件占用磁盘空间的速度也会
很快。
过一段时间之后,重新启动队列管理器或对任何损坏的对象进行媒体恢复时,就不再需
要用于线性日志的较旧的日志文件。
队列管理器定期发出一对消息,表明其需要哪个日志文件:
消息 AMQ7467 给出重新启动队列管理器所需的最旧日志文件名称。在队列管理器
重新启动过程中,此日志文件和所有较新的文件都必须可用。
消息 AMQ7468 给出进行媒体恢复所需的最旧日志文件名称。
比这些日志文件旧的任何日志文件都不必处于联机状态。您可以把它们复制到存档媒体
(如用于灾难恢复的磁带),并从活动日志目录中将其删除。还可以把媒体恢复中需要
而重新启动中并不需要的所有日志文件进行存档。
如果找不到任何需要的日志文件,就发出操作员消息 AMQ6767。对于队列管理器,使
该日志文件以及随后的所有日志文件均可用,并重试操作。
8.6.2
日志记录准则
放置和管理 MQSeries 日志时,请遵循以下准则:
在为日志文件选择位置时,请记住,如果由于缺少磁盘空间而使 MQSeries 无法格
式化新的日志,操作就会受到严重影响。
要使日志记录的效率最大化,应将日志记录卷与数据卷分开。在小型服务器上,
这可能不太实际。但是,这样做会减少队列数据记录与写入之间的争用现象。还
会消除日志和关联数据出现单一故障点的可能性。
只要有可能,就把日志文件放在以镜像形式安排的多个磁盘驱动器上。这可以避
免包含日志的驱动器发生故障。如果不采用镜像形式,您就不得不回到 MQSeries
148
系统上的最新备份。
循环日志记录管理起来最为容易,因为日志仅以循环方式重复利用。但在某些情
形下,处于此模式时,可能无法恢复。线性日志可保证恢复,但却需要进行管理,
从而避免填充所有可用磁盘空间。最后,把不再需要的线性日志存档和/或删除。
这应该是自动进行的,以免出现意外停机。
如果使用的是循环日志,请确保至少有足够磁盘空间容纳已配置的原始日志文件。
还应为至少一个次要日志文件留下空间,因为如果日志增大,就会需要此次要文
件。
如果使用的是线性日志,则应留出更多空间,因为记录数据时,日志占用的空间
会不断增大。
8.6.3
备份MQSeries资源
要备份队列管理器的数据,必须:
1.
确保队列管理器不在运行。如果正在运行,请用 endmqm 命令停止该队列管理器。
2.
查找到队列管理器放置其数据和日志文件的目录。
3.
对所有队列管理器的数据和日志文件目录(包括所有子目录)制作副本。不要丢
失任何文件,尤其是日志控制文件和配置文件。有些目录可能是空的,但如果以
后要恢复备份,就会需要这些目录,因此,建议您将其保留。
4.
确保保留文件所有权。对于 MQSeries for UNIX 系统,可通过 tar 命令实现这一点。
用 MQSeries 的 5.1 版在 Windows 上进行启动,所有 MQSeries 资源将保存在 Windows
注册表中。因此,务必在备份 MQSeries 资源时,也备份 Windows 注册表。
好的做法是把 MQSeries 资源定义保存在 MQSC 命令文件和脚本中,这样,在需要时,
就可以快速从头重新定义这些资源。
149
8.7 MQSeries Integrator系统设计
MQSeries Integrator(MQSI)是 IBM 产品。该产品由于具有通过消息转换、路由、聚集
和代理管理信息流的功能,因而可以满足商业和应用集成的需要。您还会发现 MQSI 具
有集成体系架构,其可扩充更多功能。
本节将概述 MQSI 组件以及其在 MQSI 框架中所起的作用。
MQSI 配置管理器
MQSI 代理
MQSI 用户名服务器
MQSI 控制中心
8.7.1
MQSI配置管理器角色
在任何 MQSI 域中,“配置管理器”服务是支持 MQSI 配置维护工作的实体。尽管从严
格意义上讲,配置管理器对于代理的操作并不是必需的,但配置管理器服务可能一直处
于活动状态。往来于配置管理器的队列管理器的消息通信量,限制在与代理域配置管理
相关的消息中。
所有代理操作设置的管理都是使用一个中央配置管理器服务进行的。配置管理器用来保
存代理域拓扑的详细信息以及所有消息流的定义。它还用来控制向代理网络分配和应用
消息流和其他组件。
配置管理器主持用于代理操作及消息流开发的多用户配置管理和开发平台,该平台使您
可以进行如下工作:
查看需要修改的现有 MQSI 流和/或消息设置。
把消息流重新应用到 MQSI 拓扑中的代理。
从代理执行实例中启动、停止和删除消息流。
150
这只是配置管理器可以进行的工作的一小部分。配置管理器保存 MQSI 拓扑、消息集和
消息流的全部详细信息,并能够全部或部分地重新应用 MQSI 组件。 管理员和程序员
使用 MQSI 控制中心与这些设施进行连接。
鉴于高可用性要求以及配置管理器的队列管理器所发挥的中心作用,此队列管理器是作
为 MQSeries 网络中群集仓库的绝佳选择。
8.7.2
MQSI代理角色
代理服务是 MQSI 产品的“劳役马”。实施 MQSI 一般涉及许多代理,它们可以具有不
同或相同角色。把代理应用到网络实际上是设计问题。根据配置代理所处理的流的性质,
代理的位置会影响其性能。
涉及的应用程序会影响代理拓扑的设计,以及应用到域中每个代理的消息流操作。一般
来说,来自表示层的请求采取 MQSeries 消息的形式,而 MQSeries 消息是作为对消息
流的输入而提供的。此行为启动一系列完成某种商业功能的事件。请求可能需要回复,
也可能不需要回复;如果需要回复,就可能需要同步或异步处理请求/回复的交互。很
明显,如果需要同步交互,性能就是影响针对组件分布进行决策的关键问题。
运行在代理上的应用程序可以把回复或输出消息发送(放置)到网络中的任何远程队列
及队列管理器——如果这些应用程序拥有相应授权的话。但是,应用程序只能从定义到
本地队列管理器的队列中检索(获取)消息。
为实现工作负载平衡,可以在整个网络之间复制代理队列管理器。每个代理队列管理器
拥有唯一的名称,但会包含相同的本地群集队列定义。通过使用 MQSeries 命令的脚本,
可以容易地进行以相同方式建立多个代理的管理任务。
如果接近后端应用程序数据,消息流性能会得到明显提高,因为消息流需要与历史数据
库或事务处理进行交互。如果接近客户机应用程序,且只在消息流内执行本地操作时,
响应会更加高效。很明显,这两种动态情况会发生冲突,而最佳解决方案可能是将工作
负载分为不同的子流。在这里,完整操作中会涉及不止一个代理,每个代理在其最佳位
置处理工作。在此情况下,您的配置会有不止一个代理队列管理器角色。
151
8.7.3
MQSI用户名服务器角色
用户名服务器角色为 MQSI 的发布/预订功能提供面向主题的身份验证机制。因此,如
果执行发布/预订,所有代理必须始终有权访问用户名服务器。
鉴于高可用性要求以及此队列管理器所发挥的中心作用,它是作为 MQSeries 网络中群
集仓库的绝佳选择。
在不使用发布/预订的 MQSI 环境中,无需用户名服务器服务。但是,在初始安装阶段
不会影响实施该服务,当然,在晚期阶段引入发布/预订体系架构较为容易。尽管本书
介绍的自助服务模式不需要发布/预订,但这是 MQSI 的一项主要功能,并且很可能在
将来某个时候需要实现。
8.7.4
MQSI控制中心
控制中心是用来操作 MQSI 功能和设备的图形用户界面。MQSI 管理员、技术人员以及
开发人员使用控制中心:
建立/定义代理域
创建或使用消息流
管理代理域
控制发布/预订网络
管理 MQSI 拓扑和组件的各个方面
为 MQSI 管理员、技术人员和开发人员指定适当角色
控制中心在 Windows 上运行,但可用来应用和管理运行于任何平台的代理上的消息流。
152
8.7.5
依存关系
MQSI 依赖于 MQSeries 消息发送连通性、完整性和事务处理支持设施。MQSI 组件还要
求使用数据库来存储配置和操作信息。有关详细信息,请参见《MQSeries Integrator 入
门和设计版本 2.02》编号:GC34-5599-02。
必须为将要支持应用程序的队列管理器定义队列定义。而且必须在本地给这些队列管理
器定义应用程序输入队列。
通过应用具有复制角色的许多队列管理器,可实现高可用性和负载平衡。如果某个应用
程序发现其本身无法连接到队列管理器,就可能会采取其他连接方式。应用程序代码设
计本身负责处理此路由工作。例如,可以应用一组主机,每个主机都包含 EJB 容器和队
列管理器以便处理来自 EJB 的 MQ 呼叫。
8.7.6
MQSI布置
访问数据库所需的时间可能是影响应用程序性能的重要因素。在运行 MQSI 应用程序的
同时会涉及系统数据库和用户数据库。系统数据库用来支持 MQSI 系统服务。用户数据
库包含该应用程序所需的数据。这些数据库可以是历史数据库,也可以是为该应用程序
创建的新数据库,还可以是应用程序用来进行临时存储(或缓存)的数据库。
数据库访问的频率和类型、安全考虑事项以及稳定性要求都将影响数据库具体布置到网
络什么位置。
系统数据库
MQSI 配置管理器和 MQSI 代理在数据库表中存储信息。每类信息都存储在唯一的表中。
配置管理器使用两个表,一个用作配置存储库,另一个用作消息存储库。每个代理将有
唯一的表,其中存储永久性信息。
可以把这些表组合在一个数据库中,也可以分隔为多个数据库。在讨论和实例中,我们
将假设数据库已分为各个单独的数据库,即配置数据库、消息存储库数据库以及代理数
据库。
153
必须使用 DB2 定义配置和消息存储库数据库。而代理数据库则可使用 DB2、Microsoft
SQL Server、Oracle 或 Sybase 进行定义。并非所有平台都支持上述所有类型,因此,请
检查产品随附的安装文档。
在与使用数据库的系统同样的主机上定义数据库时,尽管可明显地实现最高系统性能,
但是这些数据库可以是本地的,也可以是远程的。这样,才可以利用 XOpen/XA 设施。
代理数据库最好安置在代理设备上,在这里,由于消息流活动密集,因而性能更为重要。
不必过多考虑由于这些数据库分散所产生的管理问题(例如,集中备份能力)。如果代
理数据库中的数据毁坏或丢失,则很容易从中央配置重新应用数据库。
数据库性能在配置和消息数据库中不太重要,因为这些数据库仅用于开发和系统管理操
作活动。数据库性能对于保持这些数据库的完整性来说更为重要,因此,您可以选择把
这些数据库保存在已建立备份和安全功能的中心存储库。
用户数据库
为引用或更新现有应用程序数据库而应用的消息流,最有可能需要访问远程数据库。这
些请求通常针对历史数据,并传递到后端系统。当此类操作涉及多个步骤,也许还将涉
及许多对远程数据库的引用时,性能肯定会受到一些影响。把代理本地“缓存”数据库
用作通常访问数据的存储区,会极大地提高性能。
缓存数据库可用来实现高效地访问经常在消息流中使用的数据。在此设计中,代理本地
数据库用来支持经常访问但很少更改信息的缓存。设计使用缓存数据库的应用程序时,
必须考虑是否存在用于负载平衡的重复代理应用的可能性。在此情况下,代理可能必须
使用普通(但不一定是本地)缓存。这样,就出现缓存和数据库哪个更快的问题。
154
8.8 安全
保护资产是自助服务电子商务解决方案中的一项涵盖多个方面的任务。此环境的最终目
标是使用户能够访问商业信息。其诀窍是,在保护需要保护的资产的同时,使有权访问
商业信息的任何人均能对其进行访问。此问题的重要部分在于确定何种资源需要保护,
以及在哪个阶段。
最佳做法是,尽可能在最初阶段确定接受或拒绝访问权限,而不是等到用户在花费很长
时间深入网络后,却在最后阶段被拒绝访问某项资源。这不仅浪费系统资源,而且浪费
用户的时间。
同样重要的是,避免重复而不必要的安全检查,因为这会影响网络或应用程序的性能。
两种情形都在很大程度上取决于所使用的应用程序以及资源类型。接下来我们将指出哪
些资源需要保护、要考虑的保护类型以及可能的解决方案。
图 8-7 显示了本书讨论的“路由器”和“分解”应用模式的运行时环境。我们已确定
潜在的安全检查点。
图 8-7 安全访问控制点
155
1: 协议防火墙
协议防火墙通过按协议、访问路线、起点以及其他数据特征过滤传入的请求,从而防止
来自外部的对于 DMZ 中服务器的非授权访问。
2: Web 服务器资源
Web 服务器包含像图形文件这样的静态 HTML 页和资源。这些类型的资源一般可以放
在 DMZ 中,且安全风险极小。可使用 Web 服务器产品中包含的安全机制来保护这些资
源,这些 Web 服务器产品通常由用户、组及其可以访问的资源的硬编码列表组成。
另一种方法是使用反向代理安全服务器。Tivoli Security Policy Director 就提供了两个进
行这一级别保护的组件:
WebSEAL 组件,用于细粒度 HTTP 和 HTTPS 访问控制。
NetSEAL 组件,用于粗粒度(TCP/IP)访问控制。
使用 WebSEAL 和/或 NetSEAL 可提供一种用来防止外部客户未经身份验证和授权而
接触到任何资源的机制,不管这些资源位于 DMZ,还是位于内部网络。
WebSEAL 用作基本的 Web 服务器,并应同任何其他 Web 服务器一样受到保护,以免
遭到外部攻击。在默认情况下,WebSEAL 通过端口 80 侦听。建议您使 WebSEAL 通过
端口 80 侦听,并把 Web 服务器更改为通过另一个端口(例如,8080)侦听。这样传送
到 Web 的通信会在协议防火墙受到拦截。
3: 域防火墙
通过将传入请求限定在 DMZ 中严格控制的可信服务器列表中(例如,确立允许特定服
务器对之间进行特定类型通信的规则),域防火墙可防止未经授权的、对内部网络服务
器的访问。
4: WebSphere 应用程序服务器资源
WebSphere 包含的主要是 JSP、服务件、EJB 以及 Bean 形式的商业逻辑。应使用 J2EE
安全模型保护这些应用程序资源。使用 J2EE 安全机制,可以定义角色,并赋予角色访
问各项应用程序资源的权限。定义角色和权限是在应用程序开发级别完成的,且信息包
含在应用描述符之中。如果应用程序开发工具不具有建立应用描述符的功能,可以使用
“应用程序汇编工具”(AAT)来进行。如果目前使用的是 WebSphere Studio Site
156
Developer 或 WebSphere Studio Application Developer,则此功能已内置到工具中。
对资源的访问是通过把角色映射到方法来确定的。应用时,给角色分配实际用户和组。
通过 Web 或 EJB 容器中运行的安全协作程序,实施安全机制。
至于身份验证,WebSphere 支持使用本地操作系统 LTPA (LDAP)或自制注册表。质
询机制(WebSphere 如何从用户那里检索身份验证数据)可采取用户 ID 和密码、证书
(X.509)或自制 HTML 页的形式来检索用户 ID 和密码。
5: MQSeries 资源
MQSeries 传输用于在应用程序服务器、集成服务器以及后端系统之间移动消息。虽然
这些服务器都位于内部网络而且具有一定程度的安全性,但是在应用程序级内还需要采
取安全措施,同时需要保护队列本身的安全。这种保护机制包含授权程序,从而确保只
有授权用户可在队列上放置消息或从队列检索消息。它还可能涉及到对通过队列的消息
进行加密。
MQSeries 为其管理功能以及 MQSeries 对象访问提供安全性。此安全性依赖于基本操作
系统的安全系统所执行的身份验证。利用访问控制列表(ACL),授权用户或组可访问
MQ 资源,而 ACL 是通过 MQSeries Object Authority Manager(OAM)命令接口来维护
的。
Tivoli Policy Director for MQSeries(PD/MQ)可提供集中的系统,用来针对访问
MQSeries 资源(队列)进行定义和实施授权及数据保护策略。队列是 Policy Director 命
名空间中的对象,可以使用 Policy Director 定义的用户信息和 ACL 对其进行保护。这些
信息可用于对队列上的消息进行签名和加密。
6: MQSI 资源
MQSI 消息流使用 MQSI Control Center 进行开发、应用和控制。不同任务与预定义角色
关联,并且每个角色映射到特定的组。组中的成员确定您将履行的角色和执行的任务。
7: 后端资源
后端应用程序必须提供最后一道防御线。大部分商业逻辑和敏感数据都存储在这里,而
且各个历史应用程序将负责为这些资源提供安全性。
157
8.8.1
确保MQSeries资源的安全
由于 MQSeries 队列管理器处理具有潜在价值的信息的传输,因而您需要对权限系统进
行保护。这样可以防止队列管理器所拥有和管理的资源未经授权就被访问,因为这种访
问可能导致丢失或泄露信息。
在安全的系统中,防止所有未经授权的用户、应用程序访问或更改以下任何项目极为重
要:
与队列管理器的连接
对于诸如队列、群集、通道以及过程这样的 MQSeries 对象的访问
用于队列管理器管理的命令,包括 MQSC 命令和 PCF 命令
对于 MQSeries 消息的访问
与消息关联的上下文信息
下面一节简要概述如何完成上述任务。有关详细的 MQSeries 安全概述,请参见
《MQSeries 系统管理》编号:SC33-1873。
mqm 组
所有队列管理器进程都通过以下 ID 运行:
用户 ID
组
Mqm
mqm
提示:在 MQSeries for UNIX 系统中,UNIX 限制意味着必须使用小写字母定义所有用
户 ID。
安装期间会自动创建名为 mqm 的用户 ID,其主要组为 mqm。您可以亲自创建用户 ID
和组,但必须在安装 MQSeries 之前进行。
在 MQSeries Windows 系统上,如果本地计算机还没有本地 mqm 组,安装 MQSeries for
Windows 时就会自动创建该组。另外,还可以在域控制器上创建域 mqm 组。此全局组
可以控制 mqm 用户访问。应把此域内活动的所有特许用户 ID 添加到域 mqm 组。
管理用户 ID
要管理 MQSeries,您必须是 mqm 组的成员。尤其是,您需要此权限:
使用 runmqsc 命令运行 MQSC 命令
使用 SETMQAUT 命令管理各种权限
158
使用 crtmqm 命令创建队列管理器
如果您正在把通道命令发送到远程队列管理器,必须确保您的用户 ID 属于目标系统上
组 mqm 的成员。
OAM
在默认情况下,通过授权服务可安装组件来控制对队列管理器资源的访问,该组件称为
Object Authority Manager (OAM) for MQSeries。
OAM 随 MQSeries 提供,还可为您创建的每个队列管理器自动安装和启用,除非进行了
其他指定。
OAM 管理用户操作 MQSeries 对象的权限,包括队列和进程定义。它还提供命令接口,
通过该接口您可以授予或取消特定用户组对某个对象的访问权限。由 OAM 做出是否允
许访问某项资源的决定,而队列管理器则遵循此决定。如果 OAM 无法做出决定,队列
管理器就会阻止对该资源进行访问。
OAM 利用基本操作系统的安全功能进行工作。
尤其是,OAM 使用操作系统用户和组 ID。
用户只有具备必需的权限才能访问队列管理器对象。
通过 OAM,您可以控制:
通过 MQI 访问 MQSeries 对象。当某个应用程序试图访问对象时,OAM 将检查进
行请求的用户 ID 是否有权进行所请求的操作。特别是,这意味着可以防止队列以
及队列上的消息受到未经授权的访问。
使用 PCF 命令的权限。
提示:使用组而不是个别主角(principal)进行授权,可以减少所需的管理工作。
使组的数目尽可能少些。例如,良好的开端是,把各个组分成用于应用程序用户的一个
组以及用于管理员的一个组。
OAM 提供两个控制命令,它们使您可以管理用户权限。这两个控制命令是:
SETMQAUT
设置或重设权限
DSPMQAUT
显示权限
159
利用 SETMQAUT 命令指定的两个最常用的权限是:通过发出 MQPUT 呼叫,能够把消
息放在特定队列上(授予权限),以及通过发出 MQGET 呼叫,能够从某个队列检索消
息(获取权限)。
MQSeries Explorer 安全
启用 MQSeries Explorer 之前,必须确保所选用户具有正确的权限级别,即属于以下任
意一项:
mqm 组的成员
运行 MQSeries Explorer 的设备上的管理员组中的成员
使用 SYSTEM ID 登录
另外,有些操作可能需要您具有使用个别对象或对象类型的权限。MQSeries Explorer 使
用 MQSeries 的现有安全规则,从而确保做到该点。例如,您必须具有某个队列的显示
权限,才能在 MQSeries Explorer 中查看该队列的属性。
MQSeries Explorer 作为 MQI 客户机应用程序连接到远程队列管理器。这就意味着每个
远程队列管理器都必须具有服务器连接通道以及适当 TCP/IP 侦听器的定义。如果不为
该通道的 MCAUSER 属性指定一非空值,或者使用安全退出,恶意应用程序就可能连
接到同一服务器连接通道,并可能不受限制地访问队列管理器对象。
MCAUSER 属性的默认值为空。如果您指定一个非空用户名作为服务器连接通道的
MCAUSER 属性,使用此通道连接到队列管理器的所有程序都将以有名用户的身份运
行,并具有同样的权限级别。
MQSeries Services 安全
使用 Windows 分布式组件对象模型(DCOM)配置工具可以控制对 MQSeries Services
管理单元的访问。本书对此不做详细描述,而只是让您知道可以使用此功能。有关设置
MQSeries Services 安全的详细信息,请参见《MQSeries 系统管理》编号:SC33-1873。
要启动 DCOM 自制功能,请单击“开始”->“运行”-> “dcomcngf”。第一个窗口
将显示应用程序列表。从该列表中选择 MQSeries Services,单击“属性”,然后选择“安
全”选项卡。
160
图 8-8 使用 DCOM 自制功能的 MQSeries Services 安全
MQSeries Web Administration 安全
我们的用户 ID 需要 MQSeries 服务器上的必需管理权限来执行管理任务。因此,尝试登
录 MQSeries Web Administration 之前,请确保您具有适当的权限级别。也就是说属于以
下任意一项或多项:
mqm 组的成员
运行 MQSeries Web Administration 的设备上管理员组的成员
使用 SYSTEM ID 登录
另外,有些操作可能需要您具有使用个别对象或对象类型的权限。MQSeries Web
Administration 使用 MQSeries 的现有安全规则确保做到该点。
161
MQSeries Web Administration 使用 MQSC 连接到远程队列管理器。Web Administration
服务器代表管理员调用 MQSC 命令之前,使用每个已登录管理员的用户 ID。因此,管
理员在 MQSeries Web Administration 中具有与在 Web Administration 服务器上本地使用
runmqsc 命令完全相同的权限。
8.8.2
保护MQSI资源的安全
MQSeries Integrator 利用 MQSeries 和操作系统工具控制组件和任务的安全:
基于主题的安全。
MQSeries Integrator 用户名服务器与操作系统安全系统进行交互,以控制用户和组
对于发布和预订的访问。
组件操作控制。
MQSeries Integrator 使用操作系统访问控制。
“控制中心”中使用的操作角色。
MQSeries Integrator 使用 Windows 访问控制。
以下几节将讲述可以使用的控件,以及这些控件对于代理域操作的影响。
安全和主角(principals)
MQSeries Integrator 组件、资源以及任务的安全控制,取决于操作系统安全子系统
(Windows 用户管理器或 UNIX 用户/组数据库)的用户和用户组(主角)的定义。
安装 MQSI 时,MQSI 自动在操作系统的安全机制中创建五个组。为其中一个或多个组
指定用户以确定可执行的 MQSI 任务。MQSI 组为:
mqbrkrs
mqbrasgn
mqbrdevt
mqbrops
mqbrtpic
在 UNIX 环境中,指定给 MQSI mqbrkrs 组和 MQSeries mqm 组的用户有权执行以下任
务:
安装/卸载 MQSI。必需一个超级用户 ID(根)。
162
使用 MQSI 创建命令创建 MQSI 组件。
启动/停止 MQSeries 组件。
运行 MQSI 组件(服务 ID)。
在 Windows 环境中:
管理员组的用户可以:
-
安装和卸载 MQSI
-
创建 MQSI 组件
-
启动/停止 MQSI 组件
MQSI mqbrkrs 和 MQSeries mqm 组中的用户可以:
-
运行 MQSI 组件(服务 ID)。
必须把用户指定给除 mqbrkrs 之外的至少一个 MQSI 组,以便运行“控制中心”。
指定有用户的一个(或多个)特定组映射到“控制中心”角色,从而确定该用户
所拥有的 MQSI 权限。
表 8-4 主角与角色的关系
角色
组
消息开发者
mqbrasgn
消息分配者
mqbrdev
操作控制者
mqbrops
预订管理者
mqbrtpic
全部
mqbrasgn mqbrdev mqbrops
mqbrtpic
使用 Windows 安全域
MQSI 可从 Windows NT 本地帐户安全域、Windows NT 主域或 Windows NT 可信域中抽
取主角。有关 Windows NT 安全域的详细信息,请访问位于以下地址的 Microsoft 网站:
http://www.microsoft.com/ntserver/techresources/security/default.asp
163
MQSeries 安全
要使 MQSI 成功操作,需要许多 MQSeries 资源。您必须控制对这些资源的访问,从而
确保 MQSI 可以访问其所需资源,同时限制其他用户访问这些资源。发出命令时,系统
会代表您自动授予某些权限,主要是把消息放在队列上以及从队列检索(获取)消息的
权限。其他这类权限取决于代理域的配置。
处理 MQSI 组件队列管理器之间消息通信的传送队列,必须具有授予本地 mqbrkrs 组或
MQSI 组件的服务用户 ID 以“放置和获取全部”(put and setall)的权限。
创建、分配和应用消息流时,必须授予以下权限:
针对代理的服务用户 ID,授予 MQInput 节点中识别的每个输入队列“获取”权限。
针对代理的服务用户 ID,授予 MQutput 节点或 MQReply 节点中识别的每个输出
队列“放置”权限。
针对在其下运行接收或预订客户机应用程序的用户 ID,授予 MQInput 节点或
MQReply 节点中识别的每个输出队列“获取”权限。
针对在其下运行发送或发布客户机应用程序的用户 ID,授予 MQInput 节点中识别
的每个输入队列“放置”权限。
在《MQSeries Integrator 管理指南》编号:SC34-5792 中,将更为详细地讲述 MQSI 安
全问题。
数据库安全
必须授予“配置管理器”服务用户 ID 在定义配置和消息存储库的数据库中创建和更新
任务的权限。
必须授予每个服务用户 ID 在包含代理内部表的数据库中创建和更新任务的权限。还必
须授予每个代理服务用户 ID 对于任何已应用的消息流中消息处理节点引用和访问每个
数据库的适当访问权限。
当然,必须把对上述数据库的访问进行控制,并将其限制在指定“配置管理器”、代理
以及“用户名服务器”服务 ID 的范围之内。
164
应用程序安全
在一个或多个代理上应用消息流时,通过把消息放置到识别为输入队列的队列,应用程
序可以把消息返回到消息流之中。您可通过将队列名称设置为节点属性,在输入节点与
队列之间建立关联。
同样地,当消息流处理完这些消息后,应用程序开始访问队列,并接收 MQOutput 或
Publication 节点放置到这些队列上的消息。
因此,通过其执行应用程序的用户 ID,必须有权写入与应用程序交互的消息流使用的
队列,或有权从中进行读取。
8.9 有关详细信息
有关 Tivoli SecureWay Policy Director 的详细信息,请参见:
《Tivoli SecureWay Policy Director:集中管理电子商务安全》编号:SG24-6008
《Tivoli Policy Director 应用程序和消息发送安全》编号:SG24-6024
有关 WebSphere 应用服务器 4.0 系统管理注意事项的详细信息,请参见:
《使用 WebSphere 应用服务器版本 4.0 的自助服务模式》编号:SG24-6175
有关 MQSeries 管理和编程任务的详细信息,请参见:
《MQSeries 系统管理》编号:SC33-1873
《MQSeries 可编程系统管理》编号:SC33-1482
《MQSeries 管理界面编程指南》编号:SC33-5390
《MQSeries 命令参考》编号:SC33-1369
《MQSeries:队列管理器群集》编号:SC34-5349
《MQSeries 版本 5.1 管理和编程实例》编号:SG24-5849
165
166
第三部分
运行时实现
167
168
9
运行时安装节点
本章将讲述如何建立测试各种运行时配置的实验室环境,同时概述所用节点、平台以及
安装节点。
169
9.1 实验室环境概述
图 9-1 显示此项目所用实验室环境的物理布局。您需要记住系统名称,因为它们有助
于阅读以下各节内容,即描述如何建立各个节点。
图 9-1 实验室环境
为 该 实 验 室 的 建 立 设 计 用 户 ID 时 , 我 们 决 定 使 用 一 个 具 有 广 泛 权 限 的 用 户
ID——wasadmin,从而使整个过程变得简单。在测试环境中,这是一种可以接受的方法,
但在生产系统中,必须谨慎设计用户 ID 和权限。有关详细信息,请参阅第 155 页的 8.8
“安全性”。
9.2 重定向器节点
该环境中的 Web 服务器重定向器节点,是通过使用 IBM HTTP Server 及 WebSphere
Application Server V4.0 高级版随附的 HTTP 传输插件实现的。有关该类重定向器配置的
详细信息,请参阅《使用 WebSphere Application Server 版本 4.0 的自助服务模式》编号:
SG24-6175。本红皮书仅概述所需产品。
170
HTTP 传输插件使用 HTTP 和 HTTPS 协议,同时利用通用协议提供快速传输。
提示: WebSphere 和每种支持的 Web 服务器并非都支持 SSL 传输,具体情况取决于操
作平台。要了解每种产品是否支持 SSL,请查阅相应说明文档。
HTTP 传输插件支持各个复制品之间的循环和随机负载平衡。默认设置为循环,并可随
意选择第一个服务器。HTTP 传输插件还支持复制品的会话雷同关系(affinity),从而
确保将来自同一 HTTP 会话的 HTTP 请求发送到同一 JVM 中的同一 Web 应用程序。
HTTP 传输插件已通过 IBM HTTP Server、Microsoft IIS、Netscape iPlanet 以及 Lotus
Domino 的测试。
提示:如果是从以前的插件类型迁移到 HTTP 传输插件,就需要检查防火墙定义以确保
通信可以通过。
9.2.1
Windows 2000安装节点
下列软件将以所列的顺序安装在 Web 服务器重定向器设备中。
表 9-1 Web 服务器重定向器安装节点
产品
备注
Windows 2000 Server + SP2
需要禁用 Windows 2000 Server 随附的
Web 服务器。
IBM HTTP Server 版本 1.3.19 和 HTTP 传
从 WAS 安装媒体,选择定制安装。请
输插件
选择:
IBM HTTP Server
WebSphere 插件
IBM HTTP 插件
171
9.2.2
AIX安装节点
下面软件以所列的顺序安装在 Web 服务器重定向器设备中。
表 9-2:Web 服务器重定向安装节点
产品
备注
AIX 4.3.3
参见以下的维护列表。
IBM HTTP Server 和 HTTP 传输插件
从 WAS 安装媒体,选择定制安装。
请选择:
IBM HTTP Server
WebSphere 插件
IBM HTTP 插件
这些产品按以下顺序应用到 AIX:
X11.fnt.fontServer.4.3.3.12
devices.isa-sio.baud.rte.4.3.2.1
bos.adt.include.4.3.3.25
bos.rte.net.4.3.3.2
bos.rte.libc.4.3.3.27
bos.net.tcp.client.4.3.3.28
bos.adt.include.4.3.3.25
X11.base.rte.4.3.3.25
X11.base.lib.4.3.3.26
X11.motif.lib.4.3.3.26
9.2.3
防火墙设置
针对每个 Web 容器,打开传输设置中指定的各个端口的防火墙端口。
检查所需端口:
突出显示应用程序服务器,并打开“服务”选项卡;突出显示 Web 容器服务,并
单击“编辑属性”。
选择“传输”选项卡。您会看到在 HTTP 传输插件以及 Web 容器之间传输 HTTP
通信所用的端口号。
图 9-2 显示防火墙的逻辑设置。必须针对该插件以及 Web 容器之间传输 HTTP 所用的
每个内部端口进行重复定义。
172
图 9-2 防火墙设置
为了使用默认应用程序服务器(使用默认 HTTP 传输端口 9080),我们使用了以下防火
墙设置:
表 9-3 防火墙设置
行为
协议
Port # at
port # at
source
dest
接口
路由
方向
Permit
tcp
>1023
=9080
non-secure
route
inbound
Permit
tcp
>1023
=9080
secure
route
outbound
Permit
tcp/ack
=9080
>1023
secure
route
inbound
Permit
tcp/ack
=9080
>1023
non-secure
route
outbound
9.3 应用程序服务器
应用程序服务器节点保存修改过的 PDK-Lite WebSphere 应用程序。
173
9.3.1
Windows 2000安装节点
表 9-4 显示了针对此项目在 WebSphere Windows 2000 设备中安装的产品和功能。
表 9-4 WebSphere 版本 4.0 安装节点:Windows 2000
产品
备注
Windows 2000 Server + SP2
需要禁用 Windows 2000 Server 随附的 Web 服务器。
IBM DB2 UDB 7.2
典型安装。安装后启用 JDBC 2.0。
IBM
WebSphere
Application
使用 Windows“本地安全设置”,赋予安装/服务
Server 高级版版本 4.0
用户 ID 以下的用户权限:
IBM HTTP Server 版本 1.3.19
作为服务登录
作为操作系统的组成部分
从 WebSphere 安装媒体中选择典型安装。
JDK 1.3.0
IBM MQSeries 版本 5.2.1
典型安装
我们的网络中没有 Windows 域控制器
安装为群集存储库
MA88 SupportPac for
支持在 MQSeries 队列上获取/放置消息。MA88 包
MQSeries + IC31907
括 用 于 Java 的 MQSeries 类 以 及 用 于 JMS 的
MQSeries。
可 从 以 下 URL 下 载 SupportPac : http : / /
www.ibm.com/software/ts/mqseries/txppa
cs/ma88.html。
执行完整安装。
在 Windows 上启用包含 DB2 的 JDBC 2.0
检查系统的 JDBC 级别:
如果使用的是 JDBC 2.0,则存在文件 SQLLIB\java12\inuse。
如果使用的是 JDBC 1.0,则存在文件 SQLLIB\java11\inuse,或没有 java11 目录。
如果使用的是 JDBC 1.0,请转到 db2_root\sqllib\java12,并执行 usejdbc2.bat。
创建 WebSphere 存储库数据库
启动 WebSphere 之前,您需要创建 WebSphere 存储库数据库。在安装过程中,需要您
输入数据库名称。我们的数据库名为“WAS”。在实例中,我们打开 DB2 命令窗口,
并发出以下命令:
174
create db was
update db cfg for was using applheapsz 256
9.3.2
AIX安装节点
表 9-5 显示针对此项目在 WebSphere AIX 设备中安装的产品和功能。
表 9-5 WebSphere 版本 4.0 安装节点:AIX
产品
备注
AIX4.3.3 + ML8
如果已安装 Web 服务器,需要禁用该服务器。
IBM DB2 UDB 7.2
安装选项:
安装 DB2 Admin 客户机和 DB2 UDB EE
选择用于消息和库的 en_US
创建名为 db2inst1 的 DB2 实例
创建名为 db2as 的 DB2 Admin 服务器
没有 DB2 数据仓库
启用 JDBC 2.0。
IBM WebSphere Application
典型安装
Server 高级版版本 4.01
使用名为 WAS 的 DB2 数据库,它位于/home
/db2inst1。
IBM HTTP Server 版本 1.3.19
安装之后并在启动 WebSphere 之前,创建存储库数据
库。
JDK 1.3.0
IBM MQSeries 版本 5.2.0 +
安装所有程序包
U474779
DCE 群集存储库 SOLARSYS.CLUSTER2 除外
MA88 SupportPac for
完整安装。可从以下 URL 下载 SupportPac:
MQSeries + IC31907
http://www.ibm.com/software/ts/mqseries/
txppa cs/ma88.html。
在 UNIX 上启用包含 DB2 的 JDBC 2.0
启动 WebSphere Application Server 之前,需要更新 DB2,才能使用 JDBC 2.0。要确定
目前是否正在使用 JDBC 2.0,可以回显$CLASSPATH。如果它包含 INSTHOME/sqllib
/java12/db2java.zip,则表示正在使用 JDBC 2.0。
如果$CLASSPATH 不包含$INSTHOME/sqllib/java/db2java.zip,则使用的是 JDBC
1.0。需要执行以下操作:
切换到 DB2 实例所有者用户 ID。例如:
su db2inst1
为实例所有者在.profile 文件末尾添加以下内容:
175
if [-f /home/<db2_instance_owner>/sqllib/java12/usejdbc2 ];
then ./home/<db2_instance_owner>/sqllib/java12/usejdbc2
fi
在我们的案例中,此文件为/home/db2inst1/.profile。
创建 WebSphere 存储库数据库
启动 WebSphere 之前,您需要创建 WebSphere 存储库数据库。在安装过程中,需要输
入数据库名称。我们的数据库名为 WAS。对于 AIX 上的 WebSphere 版本和 DB2,如果
将使用本地数据库,则需要创建该数据库,然后把其当作远程数据库。在实例中,我们
使用以下命令:
su -db2inst1
create db was1
update db cfg for was1 using applheapsz 256
catalog tcpip node rs600036 remote rs600036 server 50000
db2 catalog db was1 as was at node rs600036
9.4 集成服务器
集成服务器节点保存 MQSI 代理功能。此代理执行 MQSI 消息流,这些消息流针对应用
程序执行路由器和分解功能。
第 181 页的第 10 章“MQSeries 配置”将讲述 MQSeries 的配置。第 201 页的第 11 章“MQSI
配置”将讲述 MQSeries Integrator 的配置。
9.4.1
Windows 2000安装节点
以所列的顺序安装以下软件。
表 9-6 MQSI 代理节点
产品
备注
Windows 2000 Server + SP2
IBM DB2 UDB 版本 7.1 EE
典型安装。
IBM MQSeries 版本 5.2.1
典型安装。
网络中没有 W2K 域控制器。
群集存储库。
IBM MQSI 2.02
176
完整安装。
9.4.2
产品
备注
IBM MQSI Aggregator 插 件 ,
完整安装。必须在所有代理上安装该插件,并
SupportPac IA72
将其集成到 MQSI 控制中心。
AIX安装节点
下面软件以所列顺序安装在 RS600013 上。
表 9-7 WebSphere 节点
产品
备注
AIX 4.3.3 + ML8
IBM DB2 UDB 版本 7.1 EE
安装了所有程序包(开发程序包除外)。
(MQSI
2.02 使用 DB2 7.1)
IBM MQSeries 版 本 5.2.0 +
安装了所有程序包(DCE 除外)
U474779
群集 SOLARSYS.CLUSTER2 的部分
IBM MQSI 2.02
全部安装(NEON 除外)。请参见注释 1。
IBM MQSI Aggregator 插 件 ,
完整安装。必须在所有代理上安装该插件,并
SupportPac IA72
将其集成到 MQSI 控制中心。
VisualAge for C++ runtime 5.0.2.0
把 VisualAge C++升级到此级别,以便解决
IY12087 报告的问题。
提示:安装 MQSI 之前,需要执行以下操作:
添加 mqbrkrs 组。把 mqm 和 root 放在该组中。
创建/var/mqsi 目录。
9.5 后端应用程序服务器
实例应用程序中使用两种不同类型的后端服务器。第一种类型是在 z/OS 上安装的
CICS 系统。第二种类型是运行 IBM WebSphere Application Server 版本 4.0 企业版的应
用程序服务器。企业版包括 WebSphere Application Server 版本 4.0 高级版以及扩展消息
发送支持所需的企业服务。
177
9.5.1
Windows 2000安装节点
表 9-8 显示针对此项目在 WebSphere Windows 2000 设备上安装的产品和功能。
表 9-8 WebSphere 版本 4.0 安装节点:Windows 2000
产品
备注
Windows 2000 Server + SP2
需要禁用 Windows 2000 Server 随附的 Web 服
务器。
典型安装。使用第 174 页的“在 Windows 上启
IBM DB2 UDB 7.2
用包含 DB2 的 JDBC 2.0”中的过程启用 JDBC
2.0。
IBM WebSphere Application Server
使用 Windows“本地安全设置”,赋予安装/
高级版版本 4.0
服务用户 ID 以下用户权限:
作为操作系统的组成部分
作为服务登录
IBM HTTP Server 1.3.19
从 WebSphere 安装媒体中,选择典型安装。
安装之后并在启动 WebSphere 之前,使用第 174
JDK 1.3.0
页的“创建 WebSphere 存储库数据库”中的过
程创建存储库数据库。
IBM
WebSphere
Enterprise
提供扩展消息发送支持(JMS 侦听器)。
Services
IBM MQSeries 版本 5.2.1
典型安装。
我们的网络中没有 Windows 域控制器。请为您
的网络选择适当选项。
作为群集存储库安装。
MA88 SupportPac for MQSeries +
使用 Java 支持开发消息发送应用程序。MA88
IC31907
包括用于 Java 的 MQSeries 类以及用于 JMS 的
MQSeries。可以从以下 URL 下载 SupportPac:
http : / / www.ibm.com / software / ts /
mqseries/txppa cs/ma88.html。
执行完整安装。
178
9.5.2
AIX安装节点
表 9-9 显示针对此项目在 WebSphere AIX 设备中安装的产品和功能。
表 9-9 WebSphere 4.0 安装节点:AIX
产品
备注
AIX 4.3.3 + ML8
如果已安装 Web 服务器,需要禁用该服务器。
IBM DB2 UDB 7.2
使用第 175 页的“在 UNIX 上启用包含 DB2 的
JDBC 2.0”中的过程启用 JDBC 2.0。
IBM WebSphere Application Server
典型安装。
高级版版本 4.01
位于/home/db2inst1 的 DB2 数据库 WAS。
安装之后并在启动 WebSphere 之前,使用第 176
IBM HTTP Server 版本 1.3.19
页“创建 WebSphere 存储库数据库”中的过程
创建存储库数据库。
JDK 1.3.0
IBM
WebSphere
Enterprise
提供扩展消息发送支持(JMS 侦听器)。
Services
IBM MQSeries 5.2.0 + U474779
安装所有程序包(DCE 除外)。
群集存储库 SOLARSYS.CLUSTER2。
MA88 SupportPac for
完整安装。可从以下 URL 下载 SupportPac:
MQSeries + IC31907
http : / / www.ibm.com / software / ts /
mqseries/txppa
cs/ma88.html。
179
180
10
MQSeries配置
本章将讲述如何在实验室环境中安装及执行 MQSeries。所描述的 MQSeries 基础结构基
于我们对全面应用程序解决方案的需要。该基础结构通过应用程序提供消息传输机制。
大部分配置用来支持 MQSI 代理和配置管理器。一旦此基础结构到位,您就可以继续学
习第 201 页的第 11 章“MQSI 配置”,该章将讲述如何配置 MQSI 节点。
181
10.1 应用环境
图 10-1 显示 MQSeries 基础结构概览以及运行实例应用程序所需的 MQSI 消息流。
图 10-1 实例应用程序的 MQSeries 环境
应用程序服务器节点运行前端 WebSphere 应用程序。它负责与用户连接以及驱动在集成
和后端应用程序服务器中发生的商业逻辑。此系统上的 MQSeries 配置包含 MQSeries 队
列管理器。
此环境中的集成服务器节点将运行 MQSI 代理和 MQSI 配置管理器。该集成服务器节点
包含一个 MQSeries 队列管理器和一些应用程序队列。注意:配置管理器只在 Windows
上运行,但可为运行于任何支持平台上的 MQSI 创建和控制消息流。
182
后端应用程序服务器将使用 JMS 侦听器从 MQSI 消息流接收消息。它将定义一个队列
管理器。
10.2 管理MQSeries配置
在第 134 页的 8.4“MQSeries 管理工作的管理”中简述了可用来创建和管理 MQSeries
队列的方法。就我们的目的而言,使用 MQSeries Explorer GUI 界面和 MQSC 命令是建
立 MQSeries 环境的最方便的方法。
在 Windows 系统中,我们倾向于使用 GUI 定义队列管理器和群集。我们使用了 MQSC
命令定义应用程序队列(因为我们有许多应用程序队列管理器)以及所有 AIX 环境定义。
这纯粹是我们自己做出的用户首选决定。
我们为每个 MQSeries 队列管理器创建了文本文件,其中包含配置系统所需的 MQSC 命
令。在以下讨论中,我们会经常使用这些文本文件来显示命令。这些 MQSC 命令是通
过把文本文件作为 runmqsc 命令的输入来执行的。此命令的格式是:
runmqsc qmgr_name <file_name
例如,通过把 iconfig.mqsc 文件作为输入在 SOLARSYS.QM.AS2.SERVLET2 队列管理
器上使用 runmqsc 命令,您需要输入:
runmqsc SOLARSYS.QM.AS2.SERVLET2 <iconfig.mqsc
10.3 Windows 2000环境配置
由于 Windows 环境中的设备限制,我们针对应用程序服务器节点和后端应用程序使用
了同一台设备。两种应用程序都是 WebSphere 应用程序,因此,通过把它们放在两个不
同的应用程序服务器上,我们有效地提供了单独的环境。
183
10.3.1 使用MQSeries Explorer创建队列管理器
在每个 Windows 系统上,我们使用了 MQSeries Explorer 创建以下队列管理器:
表 10-1 队列管理器
节点
队列管理器
侦听器端口
应用程序服务器
SOLARSYS.QM.AS.SERVLET
1882
集成服务器
SOLARSYS.QM.CM(仅用于 Windows):
1882
此队列由 MQSI 配置管理器使用。我们把此队列作
为默认队列管理器。
SOLARSYS.QM.BROKER1(用于 MQSI 代理)
后端应用程序服务 SOLARSYS.QM.AS
1881
1881
器(WebSphere)
创建队列管理器:
1.
依次单击“开始”–>“程序” –> “IBM MQSeries”-> “MQSeries Explorer”。
2.
右键单击“队列管理器”,并选择“新建”->“队列管理器”。
3.
在“创建队列管理器”窗口输入以下内容:
-
队列管理器名称(参见表 10-1)。
-
选择是否把此队列管理器作为默认队列管理器。
-
为停用的信件队列输入队列名称。在每个案例中,我们都使用
SYSTEM.DEAD.LETTER.QUEUE。
单击“下一步”,继续执行操作。
4.
接受日志记录默认设置,并单击“下一步”,继续执行操作。
5.
务必选中“启动队列管理器”和“创建服务器连接通道”。单击“下一
步”,
继续执行操作。
6.
选中“创建为 TCP/IP 配置的侦听器”,并输入侦听器端口号。主机和侦听器端
口的组合在网络中必须是唯一的,而且必须是不在使用的端口。
7.
单击“完成”,从而完成队列管理器创建窗口。现在,就可以在 MQSeries Explorer
窗口中看到新的队列管理器。
184
10.3.2 连接系统
此时,虽然已经定义了 MQSeries 系统和队列管理器,但它们之间尚未建立连接。我们
将 MQSeries 管理器放在称为 SOLARSYS.CLUSTER 的通用群集中,这样就可以自动建
立起连接它们所需的定义。
一个群集需要有两个队列管理器作为主要群集存储库(primary cluster repository)和次
要群集存储库(second cluster repository )。在 SOLARSYS.CLUSTER 中,我们选择
SOLARSYS.QM.AS
和
SOLARSYS.QM.CM
作
为
群
集
存
储
库
。
SOLARSYS.QM.BROKER1 和 SOLARSYS.QM.AS.ERVLET 是此群集的非存储库成员。
群 集 中 的 每 个 队 列 管 理 器 只 需 要 一 组 群 集 接 收 者 ( CLUSRCVR ) 和 群 集 发 送 者
(CLUSSDR)通道。该组通道只需定义到存储库队列管理器一次。定义完成时,队列
管理器就立即知道保存存储库信息的其他队列管理器。通过从存储库中获取建立适当队
列管理器的 CLUSRCVR 和 CLUSSDR 通道所需的信息,群集中的每个队列管理器都可
以交换信息。
我们将使用“创建群集”向导从“配置管理器”系统(集成服务器节点)定义群集。此
向导将为本地队列管理器以及第二个远程队列管理器上的群集,定义其所需的所有通道
和队列。为使此定义生效,您需要确保可以远程管理第二个系统。
确保 MQSeries 远程管理功能可用
要使 MQSeries 远程管理功能发挥作用,以下几件事情必须到位:
必须在远程系统上定义登录 Windows 时所用的用户 ID,而且必须是 Windows mqm
组(安装 MQSeries 期间创建)的成员。
每个队列管理器必须能够解析群集伙伴队列管理器的连接名称。对于 TCP/IP,
这意味着连接名称必须位于域名服务器或各个系统的/etc/hosts 文件。
远程队列管理器必须具有:
-
处于运行状态的命令服务器
-
处于运行状态的 TCPIP 侦听器
-
MQSeries SVRCONN 类型通道
185
所有这些都是在创建队列管理器时自动创建和启动的(请参见第 184 页的 10.3.1“使用
MQSeries Explorer 创建队列管理器”)。您可以使用 MQSeries Services 窗口检查命令服
务器和侦听器的状态。要启动服务,请依次选择“开始”->“程序”->“IBM MQSeries
版本 5.2.1”->“MQSeries Services”。图 10-2 显示 MQSeries Services 窗口。注意:
侦听器和命令服务器都在运行。
图 10-2 队列管理器命令服务器
在 MQSeries Explorer 窗口中可以看到服务器连接(SVRCONN)队列。在队列管理器下
选择队列。通过选择“查看” ->“显示系统对象”, 确保可看到系统对象。
图 10-3 服务器连接队列
186
命 令 服 务 器 使 用 特 殊 队 列 接 收 远 程 请 求 。 此 队 列 称 为
SYSTEM.ADMIN.COMMAND.QUEUE。在队列管理器名称下单击“队列”,就可在
MQSeries Explorer 窗口中看到此队列。此队列被视为系统对象,因此,您需要先选择“视
图”->“显示系统对象”。
图 10-4 SYSTEM.ADMIN.COMMAND.QUEUE
第 139 页的 8.4.2“远程管理”中已经讲述过远程管理。
创建群集
要创建新群集(我们称之为 SOLARSYS.CLUSTER),请执行以下操作:
1.
在“配置管理器”系统上,依次选择“开始”->“程序”->“IBM MQSeries” -
>“MQSeries Explorer”。
2.
右键单击“群集”,并选择“新建”->“群集”,启动“创建群集”向导。第一
个窗口描述即将通过的过程。阅读完此窗口后,单击“下一步”。
3.
输入群集名称 (SOLARSYS.CLUSTER)。单击“下一步”,继续执行操作。
4.
输入第一个存储库队列管理器。在我们的案例中,即 SOLARSYS.QM.CM。我们选
中此框,表明此队列管理器位于此设备的本地。
单击“下一步”,继续执行操作。
5.
输入第二个存储库队列管理器。在此案例中,我们输入 SOLARSYS.QM.AS。由于
此队列管理器处于远程,所以我们需要填入队列管理器连接名称 M23VNX63
(1881)。连接名称指定 IP 地址或 TCP/IP 主机名称,以及队列管理器的侦听器
正在运行的端口。
187
单击“下一步”,继续执行操作。
6.
下一个窗口告知您将在每个群集存储库队列管理器上定义群集发送者和接收者通
道。阅读信息,并单击“下一步”,继续执行操作。
7.
下一个窗口为主要存储库队列管理器定义群集接收者通道。在这里,向导预先填
充通道名称 TO_SOLARSYS.QM.CM 以及连接名称。请确保两个系统上的 TCP/
IP 可以解析此连接名称。
单击“下一步”,继续执行操作。
8.
此窗口为次要存储库队列管理器定义群集接收者通道。向导预先填充通道名称和连
接名称。同样地,请确保两个系统上的 TCP/IP 可以解析此连接名称。
单击“下一步”,继续执行操作。
9.
确认您的选择,并单击“完成”以创建定义。
此时,存储库队列管理器就可以彼此联系,而且您会在 MQSeries Explorer 窗口中看到
两个存储库队列管理器的当前定义。
连接群集
最后,我们需要把代理队列管理器与现有群集连接起来。我们将使用代理系统上的
MQSeries Explorer 启动“连接群集”向导。
1.
从 Explorer 窗口,右键单击该队列管理器,并选择“全部任务”->“连接群集”。
第一个窗口给出所需执行步骤的概要信息。在此窗口上,单击“下一步”。
2.
输入群集名称 (SOLARSYS.CLUSTER)。单击“下一步”。
3.
为一个群集存储库队列管理器输入队列名称和连接信息。在方案中,我们将指向
SOLARSYS.QM.CM。
188
图 10-5 连接群集窗口 4
单击“下一步”。
4.
下一个窗口包含有关所需的群集接收者和群集发送者通道的信息。阅读此信息,
并单击“下一步”。
5.
群集接收者通道名称和连接名称已预先填充好。如果满意,请接受这些默认值。
189
图 10-6 连接群集窗口 6
6.
单击“下一步”和“完成”。
这样就可创建必需的定义,并把系统连接到群集。
10.4 AIX环境的MQSeries配置
AIX MQSeries 环境包含三个节点。每个节点都安装了 MQSeries,并定义了队列管理器。
除了队列管理器名称稍有不同,AIX 环境与 Windows 环境相同。我们用一个例外创建
了完全独立的环境。我们使用以前创建的 Windows 配置管理器。AIX 和 Windows 节点
不在同一个群集中,因此必须手动进行配置管理器 Windows 设备和 AIX 之间的连接。
请参阅第 182 页上的图 10-1,以便了解该环境。
190
10.4.1 创建队列管理器
在每个 AIX 系统(应用程序服务器节点、集成服务器节点和后端应用程序节点)上,我
们使用交互式名称创建和启动以下队列管理器:
表 10-2 队列管理器
节点
队列管理器名称
侦听器端口
应用程序服务器
SOLARSYS.QM.AS2.SERVLET2
1414
集成服务器
SOLARSYS.QM.BROKER2
1414
后 端 应 用 程 序 服 务 器
SOLARSYS.QM.AS2
1414
(WebSphere)
为创建这些队列管理器,我们在每个系统上执行了以下操作:
1.
创建队列管理器:
su –mqm
cd /var/mqm
crtmqm -q queue_manager_name
2.
更新/etc/services 以添加到下行中:
MQSeries
3.
1414/tcp
#MQSeries port
更新/etc/inetd.conf 以添加到下行中:
MQSeries stream tcp nowait mqm /usr/mqm/bin/amqcrsta amqcrsta –m queue_manager_
name
重启端口监视程序(inetd)以挑选更改:
refresh -s inetd
4.
启动队列管理器:
su mqm
strmqm queue_manager_name
5.
设立服务器连接以便进行远程管理:
Runmqsc
def chl(SYSTEM.ADMIN.SVRCONN)chltype(SVRCONN)mcauser('mqm')
end
strmqcsv SOLARSYS.QM.AS2.SERVLET2
191
10.4.2 连接系统
三个 AIX MQSeries 队列管理器将参加名为 SOLARSYS.CLUSTER2 的群集。为此,我
们需要在每个系统上定义群集接收者通道和群集发送者通道(请参阅第 131 页的“群集
详细信息”)。
第一步是创建群集。我们将把后端应用程序服务器用作群集存储库,因此,我们从那里
开始。
创建群集
作为 runmqsc 命令的输入,在后端应用程序服务器 M10DF50F 上执行实例 10-1 中的命
令。
实例 10-1 设置群集
*QMID (SOLARSYS.QM.AS2)
*Make this the cluster repository (将此作为群集存储库)
ALTER QMGR +
DEADQ('SYSTEM.DEAD.LETTER.QUEUE')+
DESCR('')+
REPOS('SOLARSYS.CLUSTER2')+
REPOSNL('')+
FORCE
*Define the cluster receiver (定义群集接收者)
DEFINE CHANNEL ('TO.QMAS2')CHLTYPE(CLUSRCVR)+
TRPTYPE(TCP)+
CLUSTER('SOLARSYS.CLUSTER2')+
CONNAME('M10DF50F(1414)')+
DESCR('Cluster rcvr chl for SOLAR.QM.AS2')+
REPLACE
*Define the cluster sender (定义群集发送者)
DEFINE CHANNEL ('TO.QMBROKER2')CHLTYPE(CLUSSDR)+
TRPTYPE(TCP)+
CLUSTER('SOLARSYS.CLUSTER2')+
CONNAME('RS600013(1414)')+
DESCR('Cluster sdr chl from SOLAR.QM.AS2 to SOLARSYS.QM.BROKER2')+
REPLACE
定义群集的代理队列管理器
接下来,我们把代理队列管理器作为群集存储库,并通过在 RS600013 上执行以下命令,
将其连接到群集。
192
实例 10-2 定义群集的连接
ALTER
QMGR +
DEADQ('SYSTEM.DEAD.LETTER.QUEUE')+
DESCR('MQSI BROKER2 QMGR')+
REPOS('SOLARSYS.CLUSTER2 ')+
REPOSNL('')+
FORCE
*Define the cluster receiver (定义群集接收者)
DEFINE CHANNEL ('TO.QMBROKER2')CHLTYPE(CLUSRCVR)+
TRPTYPE(TCP)+
CLUSTER('SOLARSYS.CLUSTER2')+
CONNAME('RS600013(1414)')+
DESCR('cluster rcvr channel for SOLARSYS.QM.BROKER2')+
REPLACE
*Define the cluster sender (定义群集发送者)
DEFINE CHANNEL ('TO.QMAS2')CHLTYPE(CLUSSDR)+
TRPTYPE(TCP)+
CLUSTER('SOLARSYS.CLUSTER2')+
CONNAME('M10DF50F(1414)')+
DESCR('Clstr sdr chl fr BROKER2 to repos SOLARSYS.QM.AS2')+
REPLACE
定义群集的应用程序服务器队列管理器
最后,我们转到应用程序服务器 RS600036,并把其队列管理器添加到群集中。
实例 10-3 定义通道
*QMID (SOLARSYS.QM.AS2.SERVLET2)+
*
ALTER QMGR +
DEADQ('SYSTEM.DEAD.LETTER.QUEUE')+
DESCR('WAS Servlet QMGR')+
FORCE
*Define the cluster receiver. (定义群集接收者)
DEFINE CHANNEL ('TO.QMSV2')CHLTYPE(CLUSRCVR)+
TRPTYPE(TCP)+
CLUSTER('SOLARSYS.CLUSTER2')+
CONNAME('RS600036(1414)')+
DESCR('Cluster rcvr for SOLARSYS.QM.AS.SERVLET2')+
REPLACE
*Define the cluster sender channel. (定义群集发送者通道)
DEFINE CHANNEL ('TO.QMAS2')CHLTYPE(CLUSSDR)+
TRPTYPE(TCP)+
CLUSTER('SOLARSYS.CLUSTER2')+
CONNAME('M10DF50F(1414)')+
193
DESCR('Cluster sdr chl to SOLARSYS.QM.AS2')+
REPLACE
10.5 定义应用程序队列
用于定义 MQSI 代理和后端应用程序服务器上所需的应用程序队列的定义,与 Windows
环境和 AIX 环境中的基本相同。两种平台设置的唯一差别是队列管理器名称,它们作为
附注出现在文本文件和群集名称的开头。
定义是使用作为 runmqsc 命令的输入的文本文件进行的。
10.5.1 定义代理队列
代理主持 MQSI 消息流,并因此需要消息流中使用队列的定义。
定义 CICS 和代理之间的连接
为了触发 CICS 应用程序,我们需要定义一组从代理系统到 CISS 历史系统的发送者及
接收者通道。CICS 系统是不属于此群集的现有系统。我们仅使用标准的分布式队列排
队完成此任务。这些实例显示从 Windows 代理到 CICS 的连接的定义。通过替换正确名
称,它们对于 AIX 代理同样有效。
实例 10-4 中的定义就是在代理上执行的。
实例 10-4 定义从代理到 CICS 的队列
194
DEFINE
QLOCAL ('MQGV')+
DESCR('XMITQ TO S/390 MQGV Qmgr')+
USAGE(XMITQ)+
TRIGGER +
TRIGTYPE(FIRST)+
TRIGDATA('SOLARSYSBROKER1.MQGV')+
INITQ('SYSTEM.CHANNEL.INITQ')+
REPLACE
DEFINE
CHANNEL ('MQGV.SOLARSYSBROKER1')CHLTYPE(RCVR)+
TRPTYPE(TCP)+
DESCR('rcvr channel for S/390 Qmgr MQGV')+
REPLACE
DEFINE
CHANNEL ('SOLARSYSBROKER1.MQGV')CHLTYPE(SDR)+
TRPTYPE(TCP)+
CONNAME('WTSC47.ITSO.IBM.COM(1414)')+
CONVERT(NO)+
DESCR('sdr channel to S/390 Qmgr MQGV')+
XMITQ('MQGV')+
REPLACE
SOLARSYS_TRANSLOG 消息流队列
SOLARSYS _ TRANSLOG 消 息 流 使 用 两 个 本 地 队 列 : SOLARSYS.INPUTQ 和
SOLARSYS.INPUT.BACKOUTQ。这些队列是在代理设备上定义的。
SOLARSYS.INPUTQ 是一个由此队列管理器主持的群集队列。WebSphere 应用程序把
请求放在此队列上,并依靠其(WebSphere 设备的)本地队列管理器查找队列。队列管
理器是同一群集的组成部分,因此,如果不是在代理系统上定义队列,就无需进一步的
MQSeries 定义。
实例 10-5 SOLARSYS_TRANSLOG 消息流队列
DEFINE QLOCAL ('SOLARSYS.INPUTQ')+
DESCR('SOLARSYS_TRANSLOG input cluster queue')+
BOTHRESH(3)+
BOQNAME('SOLARSYS.INPUT.BACKOUTQ')+
CLUSTER('SOLARSYS.CLUSTER')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.INPUT.BACKOUTQ')+
DESCR('SOLARSYS_TRANSLOG BACKOUT QUEUE ')+
REPLACE
SOLARSYS_STATEMENT_REQUEST 消息流队列
SOLARSYS_STATEMENT_REQUEST 消息流的代理上需要以下定义:
实例 10-6 SOLARSYS_STATEMENT_REQUEST 的代理定义
DEFINE QREMOTE ('SOLARSYS.RATECALC.REQUEST')+
DESCR('SOLARSYS_STATEMENT_REQUEST RATECALC request queue TO CICS')+
XMITQ('MQGV')+
RNAME('SYSTEM.CICS.BRIDGE.QUEUE')+
RQMNAME('MQGV')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.STATEMENT.AGG.Q')+
DESCR('Internal Aggregate queue for SOLARSYS_STATEMENT_REQUEST')+
CLUSTER('SOLARSYS.CLUSTER')+
195
REPLACE
DEFINE QLOCAL ('SOLARSYS.STATEMENT.AGGFAILURE')+
DESCR('Aggregate failure queue for
OLARSYS_STATEMENT_REQUEST')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.STATEMENT.AGGLATE')+
DESCR('Aggregate late queue for SOLARSYS_STATEMENT_REQUEST')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.STATEMENT.AGGTIMEOUT')+
DESCR('Aggregate timeout queue for SOLARSYS_STATEMENT_REQUEST')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.STATEMENT.REQUEST')+
DESCR('Input queue for SOLARSYS_STATEMENT_REQUEST')+
CLUSTER('SOLARSYS.CLUSTER')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.RATECALC.ACK')+
DESCR('Input queue for PROCESS_RATECALC_CICS_ACK')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.RATECALC.ACK.ERROR')+
DESCR('Error queue for PROCESS_RATECALC_CICS_ACK')+
REPLACE
*these were used for testing (这些用于测试)
DEFINE QLOCAL ('SOLARSYS.INTEREST.RATE.BACKOUT')+
DESCR('back out queue for testing MAP_RATECALC_CICS_REQUEST')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.INTEREST.RATE.REQUEST')+
DESCR('Used for testing MAP_RATECALC_CICS_REQUEST')+
BOTHRESH(3)+
BOQNAME('SOLARSYS.INTEREST.RATE.BACKOUT')+
CLUSTER('SOLARSYS.CLUSTER')+
REPLACE
10.5.2 为WebSphere后端定义应用程序队列
WebSphere 后 端 应 用 程 序 需 要 这 些 定 义 。 这 些 系 统 上 的 队 列 管 理 器 为
SOLARSYS.QM.AS(Windows)和 SOLARSYS.QM.AS2(AIX)。
196
实例 10-7 SOLARSYS_STATEMENT_REQUEST 队列
DEFINE QLOCAL ('SOLARSYS.STATEMENT.GUILD.REQUEST')+
DESCR('')+
CLUSTER('SOLARSYS.CLUSTER')+
REPLACE
DEFINE QLOCAL ('SOLARSYS.STATEMENT.STATION.REQUEST')+
DESCR('')+
CLUSTER('SOLARSYS.CLUSTER')+
REPLACE
10.5.3 为WebSphere前端定义应用程序队列
WebSphere 前 端 应 用 程 序 需 要 这 些 定 义 。 这 些 系 统 上 的 队 列 管 理 器 为
SOLARSYS.QM.AS.SERVLET(Windows)和 SOLARSYS.QM.AS2.SERVLET2 (AIX)。
实例 10-8 SOLARSYS_STATEMENT_REQUEST 队列
DEFINE QLOCAL ('SOLARSYS.STATEMENT.REPLY')+
DESCR('')+
CLUSTER('SOLARSYS.CLUSTER')+
REPLACE
10.5.4 在CICS后端上定义应用程序队列
在 MQGV S/390 队列管理器上,我们需要定义这些通道:
实例 10-9 定义从 CICS 到代理的队列
DEFINE QLOCAL ('BROKER1')+
DESCR('XMITQ TO BROKER1 Qmgr on M23VNX86')+
USAGE(XMITQ)+
TRIGGER +
TRIGTYPE(FIRST)+
TRIGDATA('MQGV.SOLARSYSBROKER1')+
INITQ('SYSTEM.CHANNEL.INITQ')+
REPLACE
DEFINE CHANNEL ('SOLARSYSBROKER1.MQGV')CHLTYPE(RCVR)+
TRPTYPE(TCP)+
DESCR('rcvr channel for Qmgr M23VNX86(1881)')+
REPLACE
DEFINE CHANNEL ('MQGV.SOLARSYSBROKER1')CHLTYPE(SDR)+
TRPTYPE(TCP)+
CONNAME('M23VNX86(1881)')+
CONVERT(NO)+
197
DESCR('sdr channel to Qmgr M23VNX86(1881)')+
XMITQ('BROKER1')+
REPLACE
10.6 把AIX代理连接到Windows配置管理器
由于无法在 AIX 上运行 MQSI 配置管理器,我们将使用位于 Windows 集成服务器
M23VNX86 上的配置管理器。为此,MQSeries 基础结构必须到位。实例 10-10 上的命
令是在 AIX 集成服务器 RS600013 上执行的,用来定义此连接。
实例 10-10 将代理连接到配置管理器的队列管理器
DEFINE QLOCAL ('SOLARSYS.QM.CM')+
DESCR('xmitq to config mgr')+
USAGE(XMITQ)+
TRIGGER +
TRIGTYPE(FIRST)+
TRIGDATA('BROKER2.CM')+
INITQ('SYSTEM.CHANNEL.INITQ')+
REPLACE
DEFINE CHANNEL ('CM.BROKER2')CHLTYPE(RCVR)+
TRPTYPE(TCP)+
DESCR('rcvr from Config manager ')+
REPLACE
DEFINE CHANNEL ('BROKER2.CM')CHLTYPE(SDR)+
TRPTYPE(TCP)+
CONNAME('M23VNX86(1882)')+
XMITQ('SOLARSYS.QM.CM')+
REPLACE
这些定义与在 AIX 代理上所定义的那些相反。它们在 Windows 集成服务器上执行的,
而 Windows 集成服务器主持配置管理器的队列管理器。
实例 10-11 定义到 AIX 代理的连接
>runmqsc SOLARSYS.QM.CM
DEFINE QLOCAL ('SOLARSYS.QM.BROKER2')+
DESCR('xmitq to BROKER2')+
USAGE(XMITQ)+
TRIGGER +
198
TRIGTYPE(FIRST)+
TRIGDATA('CM.BROKER2')+
INITQ('SYSTEM.CHANNEL.INITQ')+
REPLACE
DEFINE CHANNEL ('BROKER2.CM')CHLTYPE(RCVR)+
TRPTYPE(TCP)+
DESCR('rcvr from Config manager ')+
REPLACE
DEFINE CHANNEL ('CM.BROKER2')CHLTYPE(SDR)+
TRPTYPE(TCP)+
CONNAME('RS600013(1414)')+
XMITQ('SOLARSYS.QM.BROKER2')+
REPLACE
10.6.1 从MQSeries管理AIX MQSeries资源
由于 MQSeries for AIX 不提供 MQSeries Explorer 界面,下面将介绍在 Windows 上使用
MQSeries Explorer 管理 AIX 资源的快捷方式。
确保有 SYSTEM.ADMIN.SVRCONN 队列。
1.
runmqsc SOLARSYS.QM.AS2
dis chl(SYSTEM.ADMIN*)all
使 Windows 用户能够连接到远程系统。您可通过使用以下任意一种方法达到此目
2.
的:
-
把用来登录到 Windows 的用户 ID 添加为 AIX 上的用户 ID,并将其添加到 mqm
组。
-
关闭 AIX 端的安全机制:
runmqsc SOLARSYS.QM.AS2
alter chl(SYSTEM.ADMIN.SVRCONN)chltype(SVRCONN)mcauser (‘mqm’)
refresh security(*)
end
strmqcsv SOLARSYS.QM.AS2
3.
在 Windows 上的 MQSeries Explorer 中,突出显示“队列管理器”,单击右键并选
择“显示队列管理器”。
199
图 10-7 连接远程队列管理器
退出 MQSeries Explorer 时,请保存更改。
200
11
MQSI配置
本章将讲述如何在测试实验室环境中安装和实施 MQSI。
201
11.1 创建MQSI数据库
MQSI 配置管理器和代理服务使用数据库进行管理。在案例中,我们选择为每个组件、
消息存储库、配置存储库和代理永久存储区创建不同的 DB2 数据库。
这些数据库可以位于 MQSI 组件的本地,也可位于远程系统。我们选择将所有这些数据
库放在集成服务器(代理)节点上。当决定将 MQSI 定位何处时,需要考虑性能问题。
在我们的测试实验室中,性能不是问题,但在实际网络中,性能是最重要的问题之一。
有关详细信息,请参阅第 153 页的 8.7.6“MQSI 数据库安置”。
11.1.1 创建和绑定数据库
创建数据库之后,您必须把每个数据库绑定到 CLI 程序包。在 Windows 和 AIX 系统中,
我们使用了以下 DB2 命令创建数据库并对其进行绑定。
配置管理器数据库
create db mqsicmdb
connect to mqsicmdb
bind c:\sqllib\bnd\@db2cli.lst blocking all grant public
connect reset
create db mqsimrm
connect to mqsimrm
bind c:\sqllib\bnd\@db2cli.lst blocking all grant public
connect reset
代理数据库(Windows)
create db mqsibkdb
connect to mqsibkdb
bind c:\sqllib\bnd\@db2cli.lst blocking all grant public
connect reset
代理数据库(AIX)
su db2inst1
db2 create db MQSIBK2
db2 connect to MQSIBK2
db2 bind ~/sqllib/bnd/@db2cli.lst grant public CLIPKG5
db2 update database configuration for MQSIBK2 using dbheap 900
db2 connect reset
202
提示:AIX 上的 DB2 没有本机 DB2 控制中心,但您可以容易地把远程系统(包括 AIX
DB2 系统)添加到 Windows DB2 控制中心。我们就是这样做的,并且使用 Windows DB2
控制中心授权 MQSIBK2 访问 root 和 mqm。这在测试过程中也是非常方便的。通过审
阅代理应用程序数据库的内容,我们可以检查消息流是否工作。有关这些应用程序数据
库的详细信息,请参阅第 237 页的第 14 章“构建 MQSI 应用程序”。
11.1.2 授予数据库权限
需要向运行 MQSI 服务的用户 ID 授予访问每个数据库的以下权限:
Connect
createtab
bindadd
create_not_fenced
在测试实验室中,我们使用同一用户 ID (wasadmin)创建将用来运行 MQSI 服务的数
据库,因此这是自动进行的。
如果 MQSI 服务通过不同于创建数据库时使用的用户 ID 运行,那么您可以使用以下 DB2
命令授予 MQSI 服务 ID 适当的访问权限:
connect to database (与数据库相连)
grant connect,createtab,bindadd,create_not_fenced on database to user mqsiserviceID
connect reset
11.1.3 把数据库注册到ODBC
MQSI 使用 ODBC 访问这些数据库,因此必须把这些数据库注册为 ODBC 资源。
在 Windows 上,可以使用 DB2 客户机配置助手完成此项任务。打开此工具后,选择数
据库,并单击“属性”。选择“把此数据库注册到 ODBC”以及“作为系统数据源”。
在 AIX 上,您需要修改.odbc.ini 文件。为此,请为用户(mqsi)找到.odbc.ini 文件。就
我们的案例而言,此文件位于/var/mqsi/odbc/.odbc.ini。
您需要为每个数据库添加两个条目。例如,为 MQSIBK2 数据库添加了以下两个条目:
203
[ODBC Data Sources]
MQSIBK2=IBM DB2 ODBC Driver
[MQSIBK2]
Driver=/home/db2inst1/sqllib/lib/db2.o
Description=MQSIBK2 DB2 ODBC Database
Database=MQSIBK2
11.2 创建MQSI配置管理器
每个代理域均有一个配置管理器,该配置管理器必须运行于 Windows NT 或 Windows
2000。它维护配置存储库中的配置信息,管理代理及消息处理操作的初始化和应用以响
应通过控制中心启动的操作,以及检查启动这些操作的已定义用户 ID 的权限。
我们已经定义了 MQSI 配置管理器数据库和 MQSeries 队列管理器(请参阅 第 183 页的
10.3“Windows 2000 环境配置”),因此,可以创建配置管理器。
首先,确保新的配置管理器能够访问其数据库。如果数据库位于远程系统,就需要将其
定义到容留 MQSI 配置管理器的系统。在案例中,数据库和配置管理器驻留在同一系统
上,因此无需任何操作。如果不是如此,可以使用 DB2 客户机配置助手或 DB2 编目命
令(catalog commands)进行此连接。
既然已安装和配置好所有基本组件,那么我就可以创建 MQSI 配置管理器。该配置管理
器是使用 mqsicreateconfigmgr 命令创建的。您可以从 DOS 提示符输入此命令,也可以
使用 MQSI 命令助手生成和执行该命令。我们选择使用命令助手生成配置管理器:
1.
选择“启动”->“程序”->“MQSeries Integrator 版本 2.0”->“命令助手”-
>“创建配置管理器”。
204
图 11-1 创建配置管理器窗口 1
深色框表示必填参数。输入参数时,可以看到窗口底部正在生成将要执行的命令。
在案例中,我们将使用上一步创建的队列管理器 SOLARSYS.QM.CM。
配置管理器作为 Windows 服务运行。这里指定的用户 ID 必须是 mqbrkrs 组的成员。
单击“下一步”,继续执行操作。
2.
下一个窗口配置数据库名称以及用来访问数据库的用户 ID/密码。
205
图 11-2 创建配置管理器窗口 2
单击“下一步”,继续执行操作。
3.
最后一个窗口显示将要执行的 mqsicreateconfigmgr 命令。单击“完成”,运行命
令并创建配置管理器。
现在,我们就可从 DOS 提示符或从 Windows 服务窗口启动配置管理器。服务会设置为
自动启动,因此这是最后一次需要手动启动。
在 DOS 提示符下,输入:
mqsistart configmgr
提示:创建或启动配置管理器时若发生错误,您会在应用程序日志下的“Windows 事件
查看器”中看到详细信息。
11.3 创建代理
下一步是创建一个或多个执行消息流的 MQSI 代理。您的应用程序与代理进行通信,以
便利用这些消息流提供的服务。
206
您可以在代理域内安装、创建和启动任意数量的代理。在案例中,我们在集成服务器节
点上安装和配置单一代理。
开始之前,确保已定义了代理数据库,并运行绑定以及将数据库注册到 ODBC。同时确
保为代理定义了队列管理器(请参见第 181 页的第 10 章“MQSeries 配置”)。
11.3.1 在Windows上创建代理
创建 MQSI 代理与创建配置管理器相似。使用的是 mqsicreatebroker 命令,并可以从 DOS
提示符下输入,也可以通过 MQSI 命令助手创建。我们选择了使用“命令助手”。
1.
选择“开始”->“程序”->“IBM MQSeries Integrator 2.0”->“命令助手”-
>“创建代理”。
图 11-3 创建代理窗口 1
图 11-3 中的深色框表示必填字段。每个代理分配有唯一的用户名。我们称之为
BROKER1。必须填写用来运行服务的用户 ID 和密码,就像使用 MQSeries 队列管
理器名称一样。我们将使用 SOLARSYS.QM.BROKER1。
单击“下一步”,继续执行操作。
207
2.
输入早先创建的代理数据库的名称。
图 11-4 创建代理窗口 2
单击“下一步”,继续执行操作。
3.
最后一个窗口显示将执行的 mqsicreatebroker 命令。
单击“完成”,创建该代理。
常见错误:ODBC 返回代码–1:确保针对代理数据库进行了绑定。
启动代理服务
从“Windows 服务”窗口启动代理服务。重启时,服务会自动启动,因此这是最后
一次需要进行手动启动。
11.3.2 在AIX上创建代理
在 AIX 上没有使用 MQSI 命令助手的选项。此外,使用代理之前,需要执行一些针对
MQSI 的初始安装。
1.
通过执行以下操作,为用户消息配置 syslog:
cd /var
mkdir log
208
touch /var/log/syslog.user
chown root:mqbrkrs /var/log/syslog.user
chmod 750 /var/log/syslog.user
请将下行添加到/etc/syslog.conf:
user.debug /var/log/syslog.user
提示:值可能是错误、信息、调试,警告。
2.
为 mqm 用户创建.profile。从/usr/opt/mqsi/sample/profiles/profile.aix 中获
取实例,并将其复制到/home/mqm.profile。在设置 LIBPATH 变量的行下直接添
加一个导出 LIBPATH 语句。不要给执行 DB2 .profile 的行添加附注。
#The following will set up the NLS environment(以下将设置NLS环境)
NLSPATH=/usr/lib/nls/msg/%L/%N:/usr/lib/nls/msg/en_US/%N:$NLSPATH
export NLSPATH
#Set ODBCINI to pick up the ODBC ini file(设置ODBCINI以获得ODBC ini文件)
ODBCINI=/var/mqsi/odbc/.odbc.ini
export ODBCINI
CLASSPATH=/usr/jdk_base/lib/classes.zip:$CLASSPATH
export CLASSPATH
LIBPATH=$LIBPATH:/usr/opt/mqsi/merant/lib
export LIBPATH
#If DB2 is used,the 'db2profile'script should also be run(如果使用DB2,将同样运行db2profile
脚本)
.~db2inst1/sqllib/db2profile
#
MQSI_REGISTRY=/var/mqsi
export MQSI_REGISTRY
export LIBPATH
export PATH
set -o vi
PS1='$PWD>'
export PS1 #shows the directory you are in (导出PS1#显示您所在的目录)
3.
创建代理:
mqsicreatebroker BROKER2 -i mqm -a mqm -q SOLARSYS.QM.BROKER2 -n MQSIBK2
4.
安装集成器节点。在 IA72 SupportPac 中执行安装节点。
5.
创建用户 wasadmin(如果尚未创建的话),并把此用户添加到 mqbrkrs 和 mqm 组
中。wasadmin 是用来登录到应用消息流的配置管理器设备的用户 ID。
209
6.
启动代理:
mqsistart BROKER2
检查/var/log/syslog.user 文件中的消息。这是我们定向 MQSI 特定错误消息继续在
AIX 的操作的地方。
11.4 IBM MQSI Aggregator插件
Aggregator 插件是 MQSI 的一个可单独下载的 SupportPac。该插件把节点添加到可用来
执行消息重组和分解的 MQSI 配置。必须在每个代理上安装此插件,而且新节点必须集
成到 Windows 控制中心。
按照与 SupportPac 随附的指导说明,将插件节点与 Windows MQSI 控制中心集成(如果
尚未这样做的话)。
210
12
实施JMS侦听器
本章讲述 WebSphere 的 JMS 侦听器在应用程序中的角色以及如何配置 JMS 侦听器。本
章假设您已完成 MQSeries 和 MQSI 配置步骤。此时,在实验室环境中,已经执行这些
步骤,而且已建立 MQSeries 环境。
JMS 侦听器驻留在 WebSphere 后端应用程序服务器上。其角色是侦听后续消息,并将这
些消息发送到指定会话 Bean 进行执行。
211
12.1 将WebSphere设置为使用扩展消息发送
第 177 页的 9.5“后端应用程序服务器”中已经讲述后端系统所需的软件。MQSeries 需
要为消息(队列)提供传输机制。MQSeries 的 MA88 插件提供从 Java 应用程序与 MQSeries
队列交互的能力。WebSphere Application Server 版本 4.0 高级版需要运行实际执行应用
程序商业逻辑的会话 Bean。最后,随 IBM WebSphere Application Server 版本 4.0 企业版
一起提供的 WebSphere Enterprise Services 需要提供 JMS 侦听器功能。
必须执行以下步骤,应用程序才能使用 JMS 侦听器:
定义所需的 MQSeries 基础结构
(队列和队列管理器)。第 181 页的第 10 章“MQSeries
配置”中已讲述过此步骤。
使用随 MQSeries 一起提供的 JMSAdmin 工具将队列定义到 WebSphere JNDI 命名
空间。并不是必须使用 JNDI,但我们建议这样做。第 213 页的 12.2“配置 WebSphere
JNDI 名空间”将会讲述如何执行此操作。
把 JMS 资源定义到 WebSphere(在第 223 页的 12.3.1“把 JMS 资源定义到
WebSphere”中讲述)。
创建和应用处理传入消息流的消息 Bean。第 74 页的 6.4“处理异步消息”中,已
经讲述 JMS 侦听器以及 JMS 侦听器如何使用会话 Bean 执行应用程序商业逻辑。
配置应用程序服务器扩展消息支持服务(JMS 侦听器)。请参阅第 222 页的 12.3
“为 JMS 侦听器配置 WebSphere”。
212
提示:本章将使用以下批注引用目录结构:
/mq_install_path/——安装 MQSeries 基本产品的目录。例如,在 Windows 系
统中,此目录可能是 C:\Program Files\IBM\MQSeries。而在 AIX 系统上,此目录
则很可能是/usr/mqm。
/WAS_install_path/——安装 WebSphere Application Server 高级版的目录。例
如,在 Windows 系统中,此目录可能是 C:\WebSphere\AppServer。而在 AIX 系
统中,此目录则很可能是 /usr/WebSphere/AppServer。
12.2 配置WebSphere JNDI命名空间
MQSeries 提供的 JMSAdmin 工具对管理对象非常实用。它创建目标和连接库,并将其
绑 定 到 JNDI 命 名 空 间 内 的 对 象 。 JMSAdmin 工 具 的 输 入 包 含 配 置 文 件
(JMSAdmin.config)和用户输入语句,后者要么存储在文件中,要么交互输入。
图 12-1 概述 JMSAdmin 工具的输入以及该工具所产生的输出。您很可能会在使用
MQSeries 资源的 WebSphere 系统上需要此概述。在应用程序中,我们使用 JNDI 访问应
用程序服务器以及后端应用程序服务器上的外部资源。
213
图 12-1 JMSAdmin 概述
JMSAdmin.config 文件随 MQSeries 一起提供。您只需批注不适用对您的语句。可以交互
地键入 PDKJMSConfig.scp 文本文件显示的用户输入,也可以将其作为文本文件键入。
这些语句确定将会对 JNDI 命名空间进行的更新。
12.2.1 系统准备
使用 JMSAdmin 的第一步是确定已正确配置系统环境,而且必需的文件已在适当目录
中。务必遵循与 JMSAdmin 随附的指导说明。例如,作为参考,我们将列出所执行的步
骤,但必须明白:更新 SupportPacs 的频率要比更新产品的频率高,而且您所使用的版
本及其安装步骤可能与我们所使用的不同。
MA88 SupportPac 提供的以下 JAR 文件必须对 WebSphere 应用程序服务器可用:
1.
-
214
com.ibm.mq.jar
-
com.ibm.mqjms.jar
-
com.ibm.mqbind.jar
-
providerutil.jar
-
jms.jar
-
jndi.jar
-
jta.jar
您可以使用以下任意方法达到此目的。您可以把这些文件从 mq_install_path/
Java/lib 复制到<WebSphere Install>/Appserver/lib/ext 目录。但首选方法是更
新 WebSphere 应用程序服务器的类路径。例如,在图 12-2 中,我们的应用程序使
用 Default_Server 应用程序服务器。您可以通过选定此服务器、切换到 JVM 选项
卡以及添加 JAR 文件来更新类路径。
图 12-2 更新应用程序服务器的类路径
215
AIX 用户:
把以下路径添加到 LD_LIBRARY_PATH 环境变量:
export LD_LIBRARY_PATH=/usr/mqm/java/lib
我们把此行添加到我们的 JMSAdmin 脚本。
修改/var/mqm/mqs.ini 文件,并将以下属性添加到与 WebSphere 扩展消息发送
服务共同使用的所有队列管理器:
IPCCBaseAddress=11
在 mqs.ini 文件中,搜索包含“QueueManager”的行。如果是下一行中的队列管理
器与 WebSphere 扩展消息发送服务一起使用,请把 IPCCBaseAddress 属性添加为该
队列管理器节(manager's stanza)的最后一行。
提示:删除队列管理器会删除此条目,因此,使用您的应用程序之前,请进行检查
并确保 IPCCBaseAddress 正确设置。
把以下文件从 mq_install_path/java/lib 目录复制到 /WebSphere/AppServer
/bin 目录:
-
libmqjbnd02.so
-
libMQXAi01.so
12.2.2 编辑默认JMSAdmin配置文件
默认 JMSAdmin 配置文件位于 /mq_install_path/java/bin/JMSAdmin.config:
该配置文件包含三个字段:
INITIAL_CONTEXT_FACTORY:使用此字段来指定用于创建 InitialContext 的
类名称。请记住,所有绑定都存储在一个上下文中。InitialContext 是所有绑定的基
础上下文。库名称专指目录服务供应者。下面将详细介绍适当值。
-
com.sun.jndi.ldap.LdapCtxFactory - LDAP 初 始 上 下 文 库 。 如 果 通 过 像
SecureWay 这样的 LDAP 目录存储您的管理对象,请使用此值。
-
com.ibm.ejs.ns.jndi.CNInitialContextFactory – IIOP 初始上下文库。如果通过像
VisualAge(用于 Java 的“持久名称服务器”)这样的 CORBA 目录服务供应
者存储您的管理对象,请使用此值。
-
com.ibm.ejs.ns.jndi.CNInitialContextFactory- IIOP 初始上下文库。将管理对象
存储在 WebSphere 版本 3.5 中时,使用此值。
216
-
com.ibm.websphere.naming.WsnInitialContext Factory- IIOP 初始上下文库。将
管理对象存储在 WebSphere 版本 4.x 中时,使用此值。
PROVIDER_URL:此字段指定服务供应者的 URL。例如,如果从本地设备上的 IIOP
供应者管理对象,此值将会是 iiop://localhost。
SECURITY_AUTHENTICATION:本书不使用此字段。将值保留为空。
根据我们的具体情况,实例 12-1 显示以下正确设置。
实例 12-1 JMSAdmin.config 设置
INITIAL_CONTEXT_FACTORY=com.ibm.websphere.naming.WsnInitialContextFactory
PROVIDER_URL=iiop://localhost/
SECURITY_AUTHENTICATION=none
提示:要将初始上下文库用于 JMSAdmin 的 WebSphere 版本 4.0,需要获取 e-fix
IC31907。此修复可通过 IBM 支持获取。
有关管理工具配置文件的详细信息,请参阅《使用 Java 的 MQSeries》编号:SC34-5456
-06 中的第五章。
12.2.3 建立user_dir\config.scp
可以交互地键入 JMSAdmin 的用户输入,但是把输入存储在文本文件中更为容易。在实
例中,我们将该本文文件称为 PDKJMSConfig.scp,并将其直接存储在 JMSAdmin 脚本
所在的目录中:
/mq_install_path/java/bin。
要使用 JMS 侦听器,需要添加以下绑定:
QueueConnectionFactory——WebSphere 依 赖 于 使 用 以 下 命 令 创 建 的 特 殊
QueueConnectionFactory:
InitCtx>define wsqcf(<binding name>)
动词“定义”(define)用来添加绑定。名词“wsqcf”用来指定我们为 WebSphere
添加 QueueConnectionFactory 的 MQSeries。最后,<binding name> 是用来引用
QueueConnectionFactory 的字符串的名称。
Destination——侦听器为获取消息而侦听的队列。要绑定队列的命令为:
InitCtx>define q(<binding name>)queue(<queue name>)
217
其中,“q”是绑定到名称“<binding name>”的名词。在此实例,该名词具有属性“queue”,
它代表队列“<queue name>”。
当命令输入到管理工具之后,“end”命令会终止程序。
实例 12-2 JMSAdmin 输入文件
###############################################################################
#The following JMSAdmin script will set up the appropriate contexts
#and administered objects for the necessary objects in the PDK-Lite
#application.(以下JMSAdmin脚本将为在PDK-Lite应用程序中的必要对象设置适当的内容
及管理对象。)
#
#Since we'll be using the WebSphere namespace,use
#com.ibm.websphere.naming.WsnInitialContextFactory as the
#initialContextFactory (既然我们将使用WebSphere命名空间,那么可将
com.ibm.websphere.naming.WsnInitialContextFactory作为初始上下文库。)
#
#The following contexts will be created: (将创建以下内容)
#-PDK
#-PDK/JMS
#-PDK/JMS/Statement
#-PDK/JMS/Statement/Guild
#-PDK/JMS/Statement/Stations
#
#The following administered objects will be created (Context in parentheses):
(将创建以下管理对象<上下文在括号内>)
#-(PDK/JMS/)QCF :QueueConnectionFactory
#-(PDK/JMS/Statement/Guild/)Request :Queue mapped to
#SOLARSYS.STATEMENT.GUILD.REQUEST
#-(PDK/JMS/Statement/Station/)Request:Queue mapped to
#SOLARSYS.STATEMENT.STATION.REQUEST
###############################################################################
#This is how to get back to inital context (这就是返回初始上下文的方法)
change ctx(=INIT)
define ctx(PDK)
change ctx(PDK)
define ctx(JMS)
change ctx(JMS)
define wsqcf(QCF)
define ctx(Statement)
change ctx(Statement)
define ctx(Guild)
change ctx(Guild)
define q(Request)queue(SOLARSYS.STATEMENT.GUILD.REQUEST)
#This is how to move to the above context (PDK/JMS/Statement/)
(这就是如何迁移到相关上下文——PDK、JMS、语句)
change ctx(=UP)
define ctx(Station)
change ctx(Station)
define q(Request)queue(SOLARSYS.STATEMENT.STATION.REQUEST)
218
end
在上面的实例中,所有绑定都是从“InitCtx >”表示的初始上下文进行的。但是,您可
以使用以下命令添加子上下文:
define ctx(<context name>)
其中,“<context name>”是要用来标识该子上下文的字符串。要把对象绑定到子上下
文,必须使用以下命令切换到该子上下文:
change ctx(<context name>)
要返回初始上下文,请使用以下命令:
change ctx(=INIT)
要上移一个上下文,请使用以下命令:
change ctx(=UP)
以下一系列命令可把队列 SYSTEM.DEFAULT.LOCAL.QUEUE 绑定到上下文“example
/defaultQ”。
change
define
change
define
ctx(=INIT)
ctx(example)
ctx(example)
q(defaultQ)queue(SYSTEM.DEFAULT.LOCAL.QUEUE)
12.2.4 编辑JMSAdmin脚本
调用 JMSAdmin 之前,您需要编辑 JMSAdmin 脚本文件,以确保该脚本文件可以在您的
环境中运行。您需要进行以下更改:
把以下选项添加到 Java 命令。
-
Djava.ext.dirs=/WAS_install_path/lib
对 AIX 用户的提示:在 AIX 上,还需要把以下选项添加到 Java 命令:
-Dserver.root=/usr/WebSphere/AppServer/
如果系统上尚未设置 MQ_JAVA_INSTALL_PATH 环境变量,需要将将其设置
为“mq_install_path/Java/”,作为系统变量或临时存储在脚本中。
类路径应有以下库:
-
WAS_install_path/lib/websphere.jar
219
-
-
jmsadmin_fix.jar (for the IC31907 e-fix)
mq_install_path/Java/lib/jta.jar
mq_install_path/Java/lib/com.ibm.mq.jar
mq_install_path/Java/lib/jms.jar
mq_install_path/Java/lib/com.ibm.mqjms.jar
mq_install_path/Java/lib/jndi.jar
mq_install_path/Java/Lib
/DB2_install_path/SQLLIB/java/db2java.zip
/DB2_install_path/SQLLIB/java/runtime.zip
/DB2_install_path/SQLLIB/bin
在 Windows 系统中,我们更新了 CLASSPATH 系统变量。在 AIX 系统中,我们直接从
JMSAdmin 脚本更新了 CLASSPATH 变量。
实例 12-3 显示针对 Windows 编辑过的脚本。
实例 12-3 Windows 2000 的 JMSAdmin.bat 实例
@echo off
rem -------------------------------------------
rem IBM MQSeries JMS Admin Tool Execution Script (移动IBM MQSeries JMS管理工具执行脚本)
rem for Windows NT (Windows NT移动)
rem -------------------------------------------
cls
set WAS_HOME=C:\WebSphere\AppServer
set MQ_JAVA_INSTALL_PATH=c:\program files\ibm\mqseries\java
C:\WebSphere\AppServer\java\bin\java
-Djava.ext.dirs=C:\WebSphere\AppServer\java\lib;C:\WebSphere\AppServer\java\jre
\lib\ext;C:\WebSphere\AppServer\lib;C:\WebSphere\AppServer\java\lib
-DMQJMS_LOG_DIR="%MQ_JAVA_INSTALL_PATH%"\log
-DMQJMS_TRACE_DIR="%MQ_JAVA_INSTALL_PATH%"\trace
-DMQJMS_INSTALL_PATH="%MQ_JAVA_INSTALL_PATH%"com.ibm.mq.jms.admin.JMSAdmin %1 %2 %3 %4
%5
实例 12-3 显示针对 Windows 编辑过的脚本。JMSAdmin 工具需要 JDK1.20 或更高版
本。我们把 PATH 设置为指向随 WebSphere 一起安装的 JDK。CLASSPATH 还必须更
新为指向所需的库。
实例 12-4 AIX 的 JMSAdmin.bat 实例
#--------------------------------------------
#IBM MQSeries JMS Admin Tool Execution Script
#for Unix variants (Unix变量的IBM MQSeries JMS管理工具执行脚本)
220
#--------------------------------------------
PATH=/usr/WebSphere/AppServer/java/bin:$PATH
export PATH
CLASSPATH=/usr/WebSphere/AppServer/lib/websphere.jar:/usr/mqm/java/lib/jmsadmin
_fix.jar:/usr/mqm/java/lib/jta.jar:/usr/mqm/java/lib/com.ibm.mq.jar:/usr/mqm
/ja va/lib/jms.jar:/usr/mqm/java/lib/com.ibm.mqjms.jar:/usr/mqm/java/lib/
jndi.jar:/ usr/mqm/Java/lib:/home/db2inst1/sqllib/java/db2java.zip:/home/
db2inst1/sqllib/j ava/runtime.zip:/home/db2inst1/sqllib/bin export CLASSPATH
MQ_JAVA_INSTALL_PATH=/usr/mqm/java
export MQ_JAVA_INSTALL_PATH
LD_LIBRARY_PATH=/usr/mqm/java/lib
export LD_LIBRARY_PATH
java -Djava.ext.dirs=/usr/WebSphere/AppServer/lib
-Dserver.root=/usr/WebSphere/AppServer/
-DMQJMS_LOG_DIR=$MQ_JAVA_INSTALL_PATH/log
-DMQJMS_TRACE_DIR=$MQ_JAVA_INSTALL_PATH/trace
-DMQJMS_INSTALL_PATH=$MQ_JAVA_INSTALL_PATH com.ibm.mq.jms.admin.JMSAdmin $*
12.2.5 运行JMSAdmin
可以通过以下三种方法运行 JMSAdmin:
交互式
使用文本文件输入
从 VisualAge for Java
交互式运行
以交互方式从命令行运行 JMS 管理工具并键入输入内容:
1.
启动 WebSphere Administration Server。
2.
切换到管理脚本所在目录。利用适当参数从命令行运行 JMS 管理脚本:
-
-t:启用跟踪
-
-v:启用冗余输出
221
-
-cfg <config filename>:配置文件的名称。默认名称为 JMSAdmin.config,因
此,如果没有更改该名称,就不必指定此配置文件。
例如,从 mq_install_path/java/bin 路径键入:
JMSAdmin -cfg JMSAdmin.config
如果出现“InitCtx >”提示符,就说明已成功启动管理工具。如果未启动管理工具,
请检查以下事项:
-
配置文件的值是否正确。
-
检查类路径,确保有所需的全部文件。
-
确保正确启动 WebSphere Administration Server。
如果上述方法还不能解决存在的问题,请通过把“-v”标志添加到调用管理工具
的命令,启用冗余输出。
3.
“InitCtx >”命令提示符下,键入命令(请参见第 218 页的实例 12-2)。
使用文本文件输入
通过把“< script_file”添加到调用脚本文件的命令的末尾,可以以批处理方式运行管
理工具。例如:
JMSAdmin <script_file
script_file 可以使用包含命令(用于管理工具)的任何文件。最后一个命令应该是 END
命令。从第 218 页的实例 12-2 中,可以看到应用程序中使用的脚本文件。
从 VisualAge for Java
对于通过 VisualAge for Java WebSphere 测试环境进行的测试,需要更新 VisualAge 的“持
久名称服务器”。有关进行此项操作的指导说明,请参阅第 319 页的附录 B“从 VisualAge
for Java 运行 JMSAdmin”。
12.3 针对JMS侦听器配置WebSphere
下面是安装 JMS 侦听器的几个基本步骤:
1.
使用 JMS 管理工具注册管理对象。我们刚刚在第 213 页的 12.2“配置 WebSphere
JNDI 命名空间”中进行过该过程。
222
2.
把这些对象作为 JMS 资源注册到 WebSphere 中。
3.
启用和配置扩展消息发送支持服务。
12.3.1 把JMS资源定义到WebSphere
现在,您已经把对象注册到 JNDI 命名空间,接下来需要把这些名称添加到 WebSphere。
这样,就把您的程序资源与其实际名称链接起来。可以从“资源”>“JMS 供应者”下
的管理控制台注册 JMS 资源。
通过展开 IBM MQSeries 供应者,您就会发现有一个用于 JMS 连接库和 JMS 目标的区
段。要注册某个对象,请右键单击适当区段,并选择“新建……”。
不管注册的管理对象类型如何,都需要提供相同的信息:
名称:描述对象的名称。
外部 JNDI 路径:包括相应子上下文的对象的 JNDI 名称。
说明:(可选)说明信息。
为注册实例应用程序中使用的对象,我们执行了以下步骤:
1.
从控制台中,转向“资源”>“JMS 供应者”>“IBM MQSeries”。
2.
右键单击“JMS 连接库”,并选择“新建……”。
3.
在窗口中填入适当信息。有关实例,请参见图 12-3。完成时,单击“确定”。
-
名称:PDK QueueConnectionFactory
-
外部 JNDI 路径:PDK/JMS/QCF
图 12-3 将队列连接库注册到 WebSphere
223
4.
右键单击“JMS 目标”,并选择“添加”。
5.
填入以下信息:
-
名称:PDK Guild Statement Request Queue
-
外部 JNDI 路径:PDK/JMS/Statement/Guild/Request
完成时单击“确定”。结果与图 12-4 相似。
图 12-4 将 MQSeries 队列注册到 WebSphere
利用以下信息添加另一个“JMS 目标”:
6.
-
名称:PDK Station Statement Request Queue
-
外部 JNDI 路径:PDK/JMS/Statement/Station/Request
完成时,单击“确定”。
第 218 页的实例 12-2 显示相应的 JNDI 命名空间条目。然后,在第 226 页的实例 12-
5 中,我们会看到 JMS 侦听器通过外部 JNDI 路径名称访问这些队列。可以用此路径匹
配 JNDI 命名空间中的名称,后者又映射到实际的队列名称。
12.3.2 配置扩展消息发送支持服务
设置 JMS 侦听器的最后一步是启用扩展的消息发送支持服务。JMS 侦听器是随
WebSphere Enterprise Services 一起提供的定制服务。
定制服务是应用程序服务器的属性,因此,通过选定应用程序服务器,然后在右边的属
性窗口中单击“定制”,就可以查看这些定制服务。
224
如果在“定制服务”选项卡下面看不到“扩展消息发送支持服务”这一选项,您可以通
过单击“添加……”按钮,然后键入以下属性来添加该选项:
名称——扩展消息发送支持服务
说明——(可选)
类路径——保留为空
类名称——com.ibm.cmm.listener.JMSListenerStub
配置文件 URL——保存侦听器属性的 XML 文件。
确保选中“启用”复选框。
图 12-5 显示定制服务的属性。
图 12-5 扩展消息发送支持服务的属性
225
提示:是否必须添加服务取决于创建应用程序服务器之前或之后是否安装了 WebSphere
Enterprise Service。定制服务的属性是在 XML 文件中维护的。WebSphere’s admin.config
文件中的 com.ibm.websphere.preconfiguredCustomServices 属性指向定制服务 XML 文
件列表。WebSphere 中创建新的应用程序服务器时,就会读取此列表,并把服务添加到
服务器的定制服务。在安装“企业服务”之前创建的应用程序服务器,需要手动添加定
制服务才能使用。
传递 XML 配置文件的消息
要做的最后一件事情是创建或编辑包含侦听器属性的 XML 文件。您可以在任何目录中
创建该文件。定制服务使用“配置文件 URL”属性(参见第 225 页的图 12-5)来查找
该文件。
实例 12-5 显示了用于两个 JMS 侦听器的配置文件,每个 JMS 侦听器都映射到两个单
独的队列以及两个单独的消息 Bean。
实例 12-5 JMS 侦听器的配置文件实例
<Config>
<Pooling>
<Timeout>100636</Timeout>
<Threshold>36</Threshold>
</Pooling>
<Listener>
<HomeJNDIName>com/ibm/pdk/ejb/guild/GuildMessage</HomeJNDIName>
<JMSConnectionFactory>PDK/JMS/QCF</JMSConnectionFactory>
<JMSDestination>PDK/JMS/Statement/Guild/Request</JMSDestination>
<MaxRetries>6</MaxRetries>
<MaxSessions>1</MaxSessions>
</Listener>
<Listener>
<HomeJNDIName>com/ibm/pdk/ejb/stations/StationMessage</HomeJNDIName>
<JMSConnectionFactory>PDK/JMS/QCF</JMSConnectionFactory>
<JMSDestination>PDK/JMS/Statement/Station/Request</JMSDestination>
<MaxRetries>6</MaxRetries>
<MaxSessions>1</MaxSessions>
</Listener>
</Config>
上面的实例仅显示可包括在配置文件中的属性子集。下面是完整的标记名称列表,其中
包括标记用途说明:
226
HomeJNDIName ——JMS 侦听器调用的消息 Bean 的 JNDI 名称
JMSConnectionFactory ——JMS 供应者定义的 ConnectionFactory 的 JNDI 名称
JMSDestination——侦听器将要监控的 JMS 目标的 JNDI 名称
JMSMessageSelector——确定 Bean 将接收哪些消息的字符串。
JMSAcknowledgeMode——非事务处理侦听器接收消息的确认模式。该确认模式可
以是以下各值:
-
auto-acknowledge(默认)
-
dups-ok-acknowledge
Transactional——从目标读取消息之前,侦听器是否应启动一项全局事务处理。它
可以是以下各值:
-
true(默认)
-
false
JMSDestinationType——侦听器监控的目标类型。它可以是以下各值:
-
javax.jms.Queue(默认)
-
javax.jms.Topic
MaxRetry——侦听器获取指定目标消息的最大尝试次数。默认值为“0”,表示不
再重试。
MaxSession——此侦听器在其监控的目标中可以拥有的最大会话数目。默认值为
“1”。
12.4 安装企业应用程序
下面的步骤是把消息 Bean 加载到 WebSphere 之中。消息 Bean 包含在名为 PDK-
Messaging.ear 的企业应用程序中。要把该应用程序安装在 WebSphere 之中:
1.
从控制台中,右键单击“企业应用程序”,并选择“安装企业应用程序”。
2.
在“路径”字段中,从目录结构中选定 PDK-Messaging.ear 文件。
3.
在“应用程序名称”字段中,输入“PDK-Messaging”。
227
4.
一切都应正确配置。单击“下一步”,等待出现请您选择应用程序服务器的窗口。
选择已安装 JMS 侦听器的服务器。
5.
单击“下一步”。单击“完成”。当系统询问是否重新生成应用程序代码时,请
选择“否”。
此外,应用程序需要访问后端 DB2 数据库。这是通过添加 DB2 驱动程序和指向数据库
的数据源来完成的。
12.5 启动应用程序服务器
最后一步是为 JMS 侦听器重新启动应用程序服务器。在此过程中,侦听器将打开
MQSeries 队列。观察 WebSphere 控制台,以查看是否有误。如果有问题,请检查应用
程序服务器的 stderr 和 stdout 日志。如果有错误,错误很可能属于“未找到类”(class not
found)类型(请检查应用程序服务器类路径),或者是 MQSeries 错误。请使用 MQSeries
信息中心查阅错误。
对于打开队列方面的问题,请使用 MQSeries 工具(如 amsqput)确保可以打开队列。
对于打开队列管理器方面的问题:
如果队列管理器不在本地,您可以把队列管理器名称添加到队列连接库的 JNDI 命
名空间定义。例如:
cd /usr/mqm/java/bin
./JMSAdmin
InitCtx>change ctx(PDK/JMS)
InitCtx/PDK/JMS>display wsqcf(QCF)
QMANAGER()
USECONNPOOLING(YES)
TEMPMODEL(SYSTEM.DEFAULT.MODEL.QUEUE)
MSGBATCHSZ(10)
TRANSPORT(BIND)
SYNCPOINTALLGETS(NO)
MSGRETENTION(YES)
POLLINGINT(5000)
VERSION(2)
InitCtx/PDK/JMS>alter wsqcf(QCF)QMANAGER(SOLARSYS.QM.AS2)
InitCtx/PDK/JMS>display wsqcf(QCF)
QMANAGER(SOLARSYS.QM.AS2)
USECONNPOOLING(YES)
TEMPMODEL(SYSTEM.DEFAULT.MODEL.QUEUE)
228
MSGBATCHSZ(10)
TRANSPORT(BIND)
SYNCPOINTALLGETS(NO)
MSGRETENTION(YES)
POLLINGINT(5000)
VERSION(2)
229
230
13
把WebSphere应用程序放到前端应用程序服务器上
本章将讲述在应用程序服务器节点上安装 WebSphere 应用程序所需的操作。应用程序服
务器节点与用户连接。应用程序负责显示资金、转移资金以及把消息放在 MQSI 应用程
序的队列上。
第 169 页的第 9 章“运行时安装节点”中已讲述过此服务器上所需的软件。
231
13.1 建立应用程序数据库
前端应用程序使用两个保存资金信息的数据库。GUILD 数据库有 Guild 资金的全部余额。
STATIONS 数据库保存每个行星(planet)资金信息的余额。每个数据库均使用称为 PDK
的模型。
这里并不介绍创建和加载这些数据库,因为这与应用程序的消息发送部分没有关系,但
是,我们将指导您建立从 WebSphere 到这些数据库的连接过程。
13.1.1 设置WebSphere以便访问数据库
本节假定您的计算机上已安装和运行 IBM WebSphere Application Server 版本 4.0 企业版
中的 WebSphere Enterprise Services。在此实例中,使用 WebSphere 管理控制台进行数
据库定义。
安装 DB2 驱动程序
把资金从一个数据库转移到另一个数据库时,应用程序将在数据库上进行两段式确认
(two-phase commit)。这可确保要么执行两项操作(从一个数据库取款,并存放在另
一数据库中),要么两项操作均不执行。为此,需要使用提供 JTA 支持的 DB2 驱动程
序。
从管理控制台,转到“资源” >“JDBC 提供者”。
右键单击“JDBC 提供者”,并选择“新建……”。
键入以下信息:
-
名称:2PC DB2 驱动程序
-
实现类:COM.ibm.db2.jdbc.DB2XADataSource
在“节点”选项卡上,单击“安装新建……”,并选择要安装驱动程序的节点。
完成时,单击“指定驱动程序……”。
此时,会出现提示窗口,要求包含实施指定驱动程序的文件。单击“添加驱动程
序……”,然后选择“<DB2 Installation Directory>\java \db2java.zip”。完成时,
单击“设置”。
返回在其上指定节点安装驱动程序的窗口。单击“安装”按钮。
返回“JDBC 提供者属性”窗口。单击“确定”。
232
添加数据源
在此服务器上,转移资金涉及两个应用程序数据库:GUILD 和 STATIONS 数据库。我
们需要为每个数据库定义数据源,以便确定数据库名称及用来访问数据库的驱动程序。
在刚添加的新驱动程序下,右键单击“数据源”,选择“新建……”并键入以下
1.
信息:
-
名称:2PC_STATIONS
-
JNDI 名称:jdbc/2PC_STATIONS
-
数据库名称:STATIONS
用以下信息对 GUILD 数据源重复此操作:
2.
-
名称:2PC_GUILD
-
JNDI 名称:jdbc/2PC_GUILD
-
数据库名称:GUILD
完成时,单击“确定”。
如果是远程数据库,可以选择定义访问数据库所需的用户 ID 和密码的选项。在数据源
级进行定义,使得任何应用程序都可通过该用户的权限访问数据库。也可以选择在应用
程序级设置用户 ID 和密码,例如在定义实体 Bean 的时候。
13.2 安装企业应用程序
下一步是安装应用程序服务器上需要的两个企业应用程序。“应用程序汇编工具”为此
部分应用程序产生的两个文件为 CommandServers.ear 和 PDK_Lite_EJB.ear。
1.
首先,我们安装 CommandServers.ear 文件。这是包含 TargetableCommands 支持的
应用程序:
2.
在控制台中,右键单击“企业应用程序”,并选择“安装企业应用程序”。
3.
在“路径”字段中,从目录结构中选择 CommandServers.ear 文件。
4.
在“应用程序名称”字段中,键入名称(命令服务器)。单击“下一步”,等待
出现写有“为 Web 模块选择虚拟主机”的窗口。确保选定 default_host。单击“下
一步”。
5.
在“选择应用程序服务器”窗口中,确保为两个模块都选定“默认服务器”。
6.
单击“下一步”。
233
7.
单击“完成”,当系统询问是否重新生成应用程序代码时,请选择“否”。
接下来,安装 PDK_Lite_EJB.ear 文件。这是 PDK 应用程序的前端。
1.
在控制台中,右键单击“企业应用程序”,并选择“安装企业应用程序”。
2.
在“路径”字段中,从目录结构的适当地方选择 PDK_Lite_EJB.ear 文件。
3.
在“应用程序名称”字段中,键入“PDK Lite EJB”。
4.
一切都应正确配置。单击“下一步”,等待要求您选择应用程序服务器的区段出
现。为所有模块选择默认服务器。
5.
单击“下一步”,然后单击“完成”。当系统询问是否重新生成应用程序代码时,
请选择“否”。
13.3 配置WebSphere命名空间
使用第 213 页的 12.2“配置 WebSphere JNDI 命名空间”中的过程安装 JMSAdmin 工具。
调用该工具,用以下输入文件建立队列连接库:
实例 13-1 JMSAdmin 输入文件
#This is how to get back to inital context(这就是返回初始上下文的方法)
change ctx(=INIT)
define wsqcf(gwmQCF)
define ctx(PDK)
change ctx(PDK)
define ctx(JMS)
change ctx(JMS)
define wsqcf(QCF)
define q(Request)queue(SOLARSYS.STATEMENT.REQUEST)
define q(Reply)queue(SOLARSYS.STATEMENT.REPLY)
end
在管理工具中键入命令后,end 命令就会终止该程序。
要把这些对象注册到 WebSphere:
234
1.
在控制台上,选择“资源”->“JMS 提供者”->“IBM MQSeries”。
2.
右键单击“JMS 连接库”,并选择“新建……”。
针对每项 MQSeries 资源,在窗口中填写适当信息:
3.
-
定义队列连接功能:
名称:PDK QueueConnectionFactory
外部 NDI 路径:PDK/JMS/QCF
-
定义请求队列:
名称:Request Q
外部 JNDI 路径:PDK/JMS/REQUEST
-
定义回复队列:
名称:Reply Q
外部 JNDI 路径:PDK/JMS/REPLY
提示:在应用程序中,我们对队列名称进行编码以用 (SOLARSYS.INPUTQ)作属性,
并且不为此而使用 JNDI。
235
236
14
创建MQSI应用程序
在我们的实例应用程序中使用的消息流由以下各项组成:数据库操作、后端系统消息路
由、历史格式与 XML JMS 消息之间的格式化,以及把后端回复聚集(aggregating)为
一个回复。在应用程序设计和消息流设计中,我们尽量使过程简单,并且主要目标是演
示和实施用于此模式的技术。
消息流设计的另一个目标是保持清晰。为此,我们使用的节点比实际需要的多。如果打
算进行优化应用,我们就把 ESQL 合并在可使节点数目最小化的计算、数据库和过滤器
节点之中。这有助于改进消息流的性能。
我们也设计了这些消息流,这样每个消息流可使用不同的输入队列。另一种方法是使用
同一输入队列,过滤传入的消息并把这些消息传送到预期消息流。
下面几节我们将说明如何做到:
创建事务处理日志消息流
创建语句请求消息流
创建在语句请求消息流中使用过的可复用的子流
237
14.1 使用控制中心创建应用程序
MQSI 消息流是使用“控制中心”GUI 界面创建的。控制中心仅在 Windows 上运行,可
用来把应用程序应用到任何支持 MQSI 的平台上的代理,并对这些应用程序进行监控。
14.1.1 启动控制中心
要开始创建应用程序,通过选择“开始”->“程序文件”->“IBM MQSeries Integrator
2.0”->“控制中心”来启动控制中心。此时,会显示“配置管理器连接”窗口(如图
14-1 所示),提示您输入连接到配置管理器所需的详细信息。
提示:启动“控制中心”之前,您需要转到每个 MQSI 系统,并添加在其下运行控制中
心的用户 ID。务必把用户 ID 添加到匹配将要使用角色的 MQSI 组。
图 14-1 配置管理器连接
这些字段告诉控制中心如何连接到适当的配置管理器。这些值是在创建配置管理器时确
定的。
如果尚未出现此窗口,则可能会得到错误窗口,报告连接到配置管理器时出现了问题。
如果使用控制中心与不再活动的另一个配置管理器实例协同工作,或者这是第一次打开
配置管理器,就会出现这种情况。您可能根本就看不到任何窗口。这表明与配置管理器
的连接已经生效。如果没有出现连接窗口,您可以转到“文件”->“选择连接”,这
样就会出现所需窗口。
238
单击“确定”,打开“控制中心”。
图 14-2 启动控制中心
14.1.2 创建消息流
开始消息流创建工作的一般过程是:
单击“消息流”选项卡。
右键单击“消息流”,并选择“创建”->“消息流类别”。输入新的类别名称。
并非必须创建消息流类别,而是通过它的创建提供一种组织相关消息流的方法。
该方法使您可在控制中心工作的同时,更为容易地浏览为数众多的消息流。
突出显示新的类别,单击右键,然后选择“创建”->“消息流”。
通过从左边的右窗格中复制现有节点,可在消息流中创建新的节点。可以通过双
击新节点来访问和修改新节点的属性。
对所作更改进行登记。下一次,您需要校验该节点,以便更新。
14.2 创建SOLARSYS_TRANSLOG消息流
图 14-3 显示 SOLARSYS_TRANSLOG 消息流。此消息流的目的是作为应用程序服务
器的事务处理记录器。这就消除了把事务处理记录到代理的负担,从而使 EJB 事务处理
的长度最小化。
239
图 14-3SOLARSYS_TRANSLOG 消息流
消息流的步骤如下:
接受 XML 输入消息。
从数据库检索附加数据,并将新的 XML 输出映射到 LOGENTRIES 数据库表结构。
将事务处理日志条目插入数据库。
第 81 页的 6.5.4“转移资金”中已经讲述过如何从应用程序服务器调用此消息流。当 Java
应用程序把消息放在预定为 MQSI 所用的队列上时,MQSeries 将确保消息可以到达代
理。而 MQSI 将保证记录消息,或在记录事务处理中发生问题时,把消息转送到错误队
列。
输入数据结构:transactionrecord
Java 应用程序和 MQSI 应用程序必须都能够支持将要接收的消息格式。实例 14-1 显示
将用作 SOLARSYS_TRANSLOG 消息流输入的结构。
实例 14-1 消息的输入结构
<!--transactionrecord.dtd -->
<!ELEMENT transactionrecord (station,amount,transdate)>
<!ELEMENT station (#PCDATA)>
<!ELEMENT amount (#PCDATA)>
<!ELEMENT transdate (#PCDATA)>
图 14-2 显示 XML 输入消息的实例。
240
实例 14-2 transactionrecord 数据结构
<transactionrecord>
<station>JUPITER</station>
<amount>1600</amount>
<transdate>20001-11-09 23:00:51.123000</transdate>
</transactionrecord>
输出数据库表结构
此消息流把条目放在名为 TRANSLOG 的数据库中,该数据库反映转移资金活动。条目
存储在 LOGENTRIES 表中。此数据库只由 MQSI 访问,出于性能理由,我们选择把此
数据库放在代理设备上。LOGENTRIES 表中的条目有以下 DB2 列。
列
格式
TRANSDATE
时戳
STATION
字符
AMOUNT
整数
NEWBALANCE
整数
CREATED
时戳
在第 282 页的“创建 TRANSLOG 数据库”中,将会介绍用来创建此数据库的 DB2 命令。
把输入映射到输出
您会注意到数据库中有两列:NEWBALANCE 和 CREATED,从逻辑上讲,它们不映射
到输入 XML 消息。
在第 240 页的图 14-3 中,我们可以看到 MQInput 节点(SOLARSYS.INPUTQ)的输出
节点连接到计算节点的输入(从 STATIONS 数据库获得余额)。计算节点获取 XML 输
入、从 STATIONS 数据库检索站的新余额(newbalance),以及组合两个列以产生输出
结构,数据库节点(把事务处理添加到 DB)将使用该输出结构把事务处理日志插到
TRANSLOG 数据库中。
创建表条目时,DB2 将作为当前时间戳计算 CREATED 列。
修改过的 XML 实例看起来像实例 14-3 一样。
241
实例 14-3 数据库节点的数据结构
<logentry>
<transdate>20001-11-09 23:00:51.123000</transdate>
<amount>1600</amount>
<station>JUPITER</station>
<newbalance>12000000</newbalance>
</logentry>
14.2.1 消息流详细信息
现在,我们可以仔细考察 SOLARSYS_TRANSLOG 消息流中的各个节点。
MQInput 节点:SOLARSYS.INPUTQ
该 MQInput 节点仅使用以下配置选项:
1.
把消息流根据其接收消息的队列名称设置为 SOLARSYS.INPUTQ。
图 14-4 MQInput 节点基本选项卡设置
2.
把默认消息域设置为 XML。这表示不会按一般 XML 分析输入消息,意思是,不
在 MQSeries Integrator 的消息库管理器(MRM)中定义 XML 消息。
242
图 14-5 MQInput 节点默认选项卡设置
作为一种选择,您可以为您的 XML 数据结构生成 DTD,然后使用 DTD 导入程序把 DTD
导入 MRM。您可以从以下网址下载 DTD 导入程序:
http://www-4.ibm.com/software/ts/mqseries/txppacs/id04.html
由于此应用程序中的 XML 消息相当简单,我们优先选择使用 MQSeries Integrator 的标
准功能 。
计算节点:从 STATIONS db 获取余额
除了记录从 Java 应用程序传递到消息流的事务处理信息之外,我们还想记录资金转移中
涉及的站的新余额。为此,计算节点将从 STATIONS 数据库检索该站的余额。
然后计算节点把修改过的数据映射到数据库节点希望插入 TRANSLOG 数据库中的输入
结构。图 14-6 显示计算节点的属性。
243
图 14-6 计算节点从 STATIONS db 获取余额
在“高级”选项卡上,有用来设置计算节点的选项。此设置表明将要处理通过计算节点
传递的哪些信息。选项包括“消息”、“目标”、“异常”或它们的组合。
244
图 14-7 计算节点高级属性
在此案例中,我们指定了“消息”,意思是计算节点将仅生成节点中代码指定的新的消
息部分。其他组件将通过未更改的计算节点进行传递。
数据库节点:把事务处理添加到 DB
数据库节点将获取计算节点生成的新消息,并使用 SQL 把数据插到 TRANSLOG 数据
库的 LOGENTRIES 表中。
245
图 14-8 数据库节点把事务处理添加到 DB
14.3 创建SOLARSYS_STATEMENT_REQUEST消息流
此消息流使用可从 MQSeries SupportPacs 网站下载的受支持的插件节点。您可以从以下
网址找到 IBM MQSI Aggregator 插件 SupportPac IA72:
http://www-4.ibm.com/software/ts/mqseries/txppacs/ia72.html
有关此插件所提供功能的一般说明,请参阅第 117 页的 7.3.7“IBM MQSI Aggregator 插
件”。
图 14-9 显示 SOLARSYS_STATEMENT_REQUEST 消息流。
246
图 14-9 SOLARSYS_STATEMENT_REQUEST 消息流
此消息流的目的是:
从 SOLARSYS.STATEMENT.REQUEST 队列获取语句请求。
将消息拆分为三个后端请求。
将后端回复关联为一个回复,以便将其发送回请求用户或应用程序。
输入和输出数据结构均为 XML 消息。
247
输入数据结构:视图-语句-命令
实 例 14 - 4 中 显 示 的 输 入 数 据 是 从 用 户 界 面 和 WebSphere 层 生 成 的 , 并 由
ViewStatementCommandImpl 传递到 MQSI 流(请参阅第 92 页的 6.6.5“把消息放到队列
和检索消息上”)。此 XML 数据将在子流中用作后端请求的输入。
实例 14-4 输入数据结构
<view-statement-command>
<user>
<station>MARS</station>
<role>Administrator</role>
</user>
<interest-rate>
<type>NEW</type>
<year>2000</year>
<credit-rating>2</credit-rating>
<duration>48</duration>
</interest-rate>
</view-statement-command>
同一消息将通过第 247 页的图 14-9 显示的所有三个节点 SpAggCreate1、SpAggCreate2
和 SpAggCreate3 进行发送。这些节点获取这些消息,并将其映射或转换为后端系统所
需的格式。
输出数据结构:STATEMENT_REPLY
这是聚集器放到 SpAggregateReply 节点 out 输出终端的相互关联的响应消息。如第 247
页的图 14-9 所示,我们把 SpAggReply1 节点的 out 终端连接到 “STATEMENT REPLY
Q” MQReply 节点前的一个计算节点,这样,我们就可以把其余信息添加到输出数据结
构。
实例 14-5 中显示输出消息实例。
实例 14-5 输出数据结构
<STATEMENT_REPLY>
<translog>
<TRANSDATE>11-04-2001 19:33:37.067000</TRANSDATE>
<STATION>JUPITER</STATION>
<AMOUNT>20000</AMOUNT>
<NEWBALANCE>120000000</NEWBALANCE>
<CREATED>11-04-2001 19:33:37.068000</CREATED>
</translog>
248
<STATEMENT_REPLY10>
<solarsys-statement-reply>
<name>None</name>
<balance>20000</balance>
</solarsys-statement-reply>
</STATEMENT_REPLY10>
<STATEMENT_REPLY20>
<solarsys-statement-reply>
<name>GRANTS</name>
<balance>504099</balance>
</solarsys-statement-reply>
</STATEMENT_REPLY20>
<STATEMENT_REPLY30>
<InterestReply>
<Type>NEW</Type>
<Year>2000</Year>
<Duration>48</Duration>
<Rate>673</Rate>
</InterestReply>
</STATEMENT_REPLY30>
</STATEMENT_REPLY>
14.3.1 消息流详细信息
现在,我们就可以看看 SOLARSYS_STATEMENT_REQUEST 消息流的每个节点。
MQInput 节点:SOLARSYS.STATEMENT.REQUEST
MQInput 节点的设置与第 242 页的“MQInput 节点:SOLARSYS.INPUTQ”中显示的节
点相似。在此案例中,将从 SOLARSYS.STATEMENT.REQUEST 队列中检索消息。消
息域属于 XML。
计算节点:CorrelId=Messageid
消息流功能不需要此计算节点,只是出于测试目的把它包含在内。此计算节点确保
ReplyToQ、ReplyToQMgr 和 CorrelId 具有正确的值。当消息流在整个电子商务应用程
序的上下文内工作时,生成请求的 Java 应用程序确保针对标准的 MQSeries 请求回复设
置这些值。出于测试目的,我们同时会使用作为{MQSeries Root}\bin 目录组成部分的
amqapi 可执行程序。此实用程序赋予您获取和放置 MQSeries 消息的完全 MQI 功能。
249
SpAggregateCreate 节点:SpAggCreatex
此消息流中的 SpAggCreate1、2 和 3 节点属于 SpAggregateCreate 节点类型。安装聚集器
插件时也会把此节点类型包含在内。
CorrelId=MessageID 节点的 out 终端(out terminal)连接每个创建节点。图 14-10 显示
SpAggregateCreate 节点的基本配置属性。SpAggCreate2 和 SpAggCreate3 的设置是相同
的,只是序列号分别设置为 20 和 30。讨论 SpAggregateReply 节点时,我们将会看到如
何使用序列号,但在此,我们只需知道序列号是选择的随机数,而且每个
SpAggregrateCreate 节点的序列号都是唯一的。
AggregateName 用来把 SpAggregateCreate 节点与 SpAggregateReply 节点链接起来。
图 14-10 SpAggCreate1 基本选项卡
这些节点把消息作为扇出消息组的组成部分,并与消息组相关。不要把“组”的上下文
与 MQSeries 消息组相混淆。
如果配置适当,SpAggregateCreate 节点与 SpAggregateReply 节点就可以协调工作。
SpAggregateCreate 节点把信息存储在集合控制块中,这样,SpAggregateReply 节点就可
以把来自后端应用程序的回复组合成一个回复,并返回到请求应用程序。
250
提示:从 SpAggregateCreate 节点的 out 终端更改消息标题时,一定要谨慎。聚集器节点
要求您使用标准的 MQSeries 编程惯例。当应用程序检索请求时,应用程序应将其检索
的消息 ID 放到其发回的回复消息的关联 ID 之中。您可以采用后端应用程序所接受的任
何所需方法(这在后面的内容中将会讲述)映射消息数据部分。
如果无法做到这一点,还有其他选择。例如,您将会在第 262 页的 14.3.3“MAP_
RATECALC_CICS_REQUEST 子流”中看到一种选择。
一般来说,当您的消息经过 SpAggregateCreate 节点时,会发生以下事件:
记录 MQMD 消息 ID、关联 ID、ReplyToQ 和 ReplyToQMgr 设置。
用新的消息 ID 取代消息 ID 值。这一新的消息 ID 对于根据原型创建的每个新消息
都是相同的。
关联 ID 在内部用作集合标识符。每条输入消息必须包含一个关联 ID。
ReplyToQ 值为 SpAggregateReply 节点的队列所取代。
ReplyToQMgr 值为代理的队列管理器名称所取代。
流经 SpAggregateCreate 节点的每条消息都将设置/重设和启动计时器。
接收这些消息的后端应用程序应遵循标准的 MQSeries 编程惯例:将其接收的消息 ID 放
到其发回的回复消息的关联 ID 之中。后端应用程序还应使用 MQMD ReplyToQ 和
ReplyToQMgr 值 , 以 便 确 定 把 回 复 发 送 到 什 么 地 方 。 在 此 案 例 中 , 这 个 地 方 为
SpAggregateReply 节点的队列。
SpAggregateReply 节点:SpAggregateReply1
由于理解 SpAggregateReply 节点对于理解 SpAggregateCreate 节点功能极为重要,因此,
现在我们来看看该节点。
SpAggregateReply 节点负责把回复从后端应用程序聚集到返回请求应用程序或用户的一
个回复中。为此,该节点使用 SpAggregateCreate 节点填充的集合数据库(aggregate
database)中存储的信息,同时还使用其自己属性中设置的其他参数。
251
图 14-11 和图 14-12 显示 SpAggregateReply1 节点的基础和高级属性。
图 14-11 SpAggregateReply1 节点基础选项卡
消息流中所有 SpAggregateCreate 节点以及关联的 SpAggregateReply 节点都具有同一个
AggregateName。如第 250 页的图 14-10 所示,AggregateName 值与此节点中的值相同。
ReplyTimeoutSeconds 是聚集器等待所有回复返回到此节点的 ReplyToQueue 中指定的
SpAggregateReply 节点队列的时间。
ReplyToQueue 是后端应用程序应回复的队列。SpAggregateReply 节点使回复消息与根据
关联 ID 发出的消息相匹配,而且关联 ID 应是在 SpAggregateCreate 节点中设置的消息
ID。聚集器以前从未遇到过的任何关联 ID 可直接到达 lateReplies 终端。
图 14-12 SpAggregateReply1 高级选项卡
252
AggregateReplyDestination 是允许开发者表明应如何处理聚集回复消息的枚举列表。由
于我们接受的是来自前端的回复,并且通过对用户的聚集响应进行回复(标准的
MQSeries 请求/回复编程),所以我们选择了 ReplyToQueue。
AggregatedReplyDataType 是另一个枚举列表,它使开发者可以选取输出消息的格式。我
们选择了 XML。
AggregatedReplyLabelName 是 SpAggregateReply 节点将与 SpAggregateCreate 节点的
SequenceNumber 联合使用的值,目的是为了创建使每个回复回行的 XML 标记名称。第
248 页的“输出数据结构:STATEMENT_REPLY”中显示输出数据结构的实例。
返回 SpAggregateReply 节点的每个回复消息都会导致 SpAggregateReply 节点执行以下
逻辑:
使用此回复消息关联 ID,可在聚集器存储库(aggregator repository)中查找消息的
信息。同样地,这表示后端应用程序正在使用传入的消息 ID 设置传出消息关联 ID。
从原始请求消息存储的队列设置 ReplyToQ 和 ReplyToQMgr。
对每个带有从 AggregatedReplyLabelName 属性派生的标记名称的回复消息进行回
行,该 AggregatedReplyLabelName 属性与回复序号连接。
创建包含 AggregatedReplyLabelName 值的顶级标记。
设置聚集消息的 MessageID 和 CorrelId,这样,就可以在把“Advanced.Destination
Mode”属性设置为“ReplyToQueue”的情况下,通过 MQReply 节点或 MQOutput
节点进行处理。
到达后端应用程序的每条消息都经过 SpAggregateCreate 节点。这会把计时器设置为
SpAggregateReply 节点中表明的值。如果 SpAggregateReply 节点按时接收所有回复,就
通过 out 终端把聚集的回复路由出去。如果计时器在所有回复返回之前到期,就通过
timedOut 终端把迄今为止已聚集的回复路由出去。如果回复在计时器到期之后到达,就
会把这些回复路由到 lateReplies 终端。
在把回复路由到 out、timedOut 或 lateReplies 终端之前,SpAggregateReply 节点恢复
ReplyToQ 和 ReplyToQMgr,并把消息描述符中的 CorrelId 设置为请求消息的消息 ID,
该消息 ID 由 SpAggregateCreate 节点存储。
253
MQOutput 节点
第
247
页
中
图
14
-
9
中
显
示
的
三
个
SOLARSYS.STATEMENT.STATION.REQUEST
输
出
节
点
、
SOLARSYS.STATEMENT.GUILD.REQUEST 和 SOLARSYS.RATECALC.REQUEST 很
相似。它们分别表示 MAP STMNT CMD REQ1、MAP STMNT CMD REQ2 和 MAP_
RATECALC_CICS_REQ 子流的输出队列。稍后,在第 259 页的 14.3.2“子流:MAP
STMNT CMD TO REQUEST”中将描述这些子流。
前 两 个 子 流 均 会 将 某 个 请 求 发 送 到 应 用 程 序 , 要 求 站 和 Guild 的 余 额 信 息 。
SpAggregateCreate 节点把 ReplyToQ 和 ReplyToQMgr 更改为 SpAggregateReply 节点的
队列。
第三个子流 MAP_RATECALC_CICS_REQ
(如第 262 页的 14.3.3“MAP_RATECALC
_CICS_REQUEST 子流”中所示)将会把某个请求发送到应用程序,要求进行基于贷
款的信用评级、类型和时间长度的个性化利率计算。此 SpAggregateCreate 节点保存有
原始 ReplyToQ、 ReplyToQMgr 以及其他信息,并设置 ReplyToQ 和 ReplyToQmgr 值,
这样回复就可以到达第 269 页的 14.3.4“PROCESS_RATECALC_CICS_ACK”中描
述的消息流。
图 14-13 显示为 SOLARSYS.STATEMENT.STATION.REQUEST 指定的基本属性。为
简单起见,我们使用了匹配 MQOutput 节点名称的队列名称。
图 14-13 MQOutput 节点 SOLARSYS.STATEMENT.STATION.REQUEST
每个 MQOutput 节点的高级属性目标模式都设置为“队列名称”。也就是说,会把消息
发送到“队列名称”属性中命名的队列(而不是 ReplyToQ 字段或目标列表中的队列)。
254
图 14-14 高级属性
计算节点:更改 MQMD 格式
此节点需要 SpAggregateReply1 节点的 out 终端的输入。如果在计时器到期之前收到所
有回复,就会把聚集的消息路由到 SpAggregateReply1 的 out 终端。在消息发送到
MQReply 节点(STATEMENT.REPLY.Q)之前,我们需要更改 MQSeries 标题,并把事
务处理日志信息添加到输出数据结构。
255
图 14-15 计算节点:更改 MQMD 格式
在“高级”选项卡中,我们已经把计算模式设置为“消息”,意思是计算节点将修改消
息。
让我们仔细查看 ESQL 中执行的功能。
实例 14-6 中显示的 ESQL 把消息标题的格式设置为字符串。
实例 14-6:把消息标题的格式设置为字符串
SET OutputRoot.MQMD.Format =MQFMT_STRING;--'MQSTR ';
256
之所以必须这样做,是因为后端应用程序使用 JMS 创建的 MQSeries 消息将具有预先附
加到消息的 MQRFH2 标题,并且将具有 MQMD.Format 字段中的类型 MQRFH2。当聚
集器从后端应用程序到来时,就使用 MQMD.Format 字段。我们把此格式更改为
STRING,这样接收聚集回复的 JMS 层就不会错误地解释 MQRFH2 标题。
实例 14-7 显示数据库 SQL,我们使用它把新的 XML 文件夹 translog 添加到现有的顶
级 XML 文件夹“STATEMENT_REPLY”。这将会为此站包含从 LOGENTRIES 表检
索的事务处理记录的数据。
实例 14-7 计算节点将 translog 添加到 ESQL
SET OutputRoot.XML."STATEMENT_REPLY"."translog"[]=(
SELECT L.TRANSDATE,L.STATION,L.AMOUNT,L.NEWBALANCE,L.CREATED
FROM Database.LOGENTRIES AS L
WHERE L.STATION =
InputRoot.XML."STATEMENT _ REPLY"."STATEMENT _ REPLY10"."solarsys - statement - reply"."name" ) ; IF
OutputRoot.XML."STATEMENT_REPLY"."translog"IS NULL THEN
SET OutputRoot.XML."STATEMENT_REPLY"."translog"='No transaction activity found';(没有发现事务处理
活动)
END IF;
MQReply 节点:STATEMENT REPLY Q
此节点的 in 终端与 Change MQMD Format 计算节点的 out 终端连接。
图 14-16 MQReply 节点:STATEMENT REPLY Q
如果后端请求一切顺利,这就是我们要结束工作的节点。此节点将使用标准的 MQSeries,
并会把聚集的回复消息发送到原始请求消息中指定的队列和队列管理器,而原始请求消
息已经到达 SOLARSYS.STATEMENT.REQUEST 节点。
257
MQOutput 节点:SOLARSYS.STATEMENT.AGGFAILURE
此节点的 in 终端连接 SpAggregateReply1 节点的 failure 终端。之所以在此路由消息,是
因为处理节点过程中发生错误。
图 14-17 MQOutput 节点:SOLARSYS.STATEMENT.AGGFAILURE
“高级”属性把目标模式设置为“队列名称”,意思是把消息放到“队列名称”属性指
定的队列上。
MQOutput 节点:SOLARSYS.STATEMENT.AGGLATE
如果 SpAggregateReply1 节点接收到计时器到期之后返回的消息,此节点就在这里路由
消息。
图 14-18 MQOutput 节点:SOLARSYS.STATEMENT.AGGLATE
“高级”属性把目标模式设置为“队列名称”。
MQOutput 节点:SOLARSYS.STATEMENT.AGGTIMEOUT
如果计时器到期,SpAggregateReply1 节点就会在这里路由到期后聚集的消息。
258
图 14-19 MQOutput 节点:SOLARSYS.STATEMENT.AGGTIMEOUT
在“高级”属性中,目标模式设置为“队列名称”。
14.3.2 子流:MAP STMNT CMD TO REQUEST
图 14-20 显示 MAP STMNT CMD TO REQUEST 子流。此消息流的目的是把输入消息
格式化为从站和 Guild 获取余额信息的后端请求。由于这些请求的输入和输出结构相同,
因此我们决定重复使用逻辑。
此子流在第 247 页的图 14-9 中显示的主消息流中使用两次:一次作为 MAP STMNT
CMD TO REQ1,发送要求站信息的请求;第二次作为 MAP STMNT CMD TO REQ2,
检索 Guild 信息。
图 14-20 MAP STMNT CMD TO REQUEST 子流
259
此流基本上包含一个计算节点,它把数据转换为新的格式。可以把此处理压缩到消息流
中,但为清楚起见,我们把它拆分开来。如果要考虑性能问题,我们肯定会把此节点压
缩并合并到 SOLARSYS_STATEMENT_REQUEST 消息流之中。
输入数据结构
此子流的输入数据结构与前面第 248 页的“输入数据结构:视图-语句-命令”中描述
的输入数据结构相同。输入数据所包含的信息比此节点需要获取所寻找的信息要多。
输出数据结构:solarsys-语句-请求
实例 14-8 显示后端请求的 XML 输出结构。这是从此节点的 STMNT REQUEST OUT
终端返回的数据结构。该结构基本上从原始请求中去除了利率信息。
实例 14-8 子流的输出数据结构
<solarsys-statement-request>
<name>MARS</name>
<role>admin</role>
</solarsys-statement-request>
回复数据结构:solarsys-语句-回复
实例 14-9 显示来自后端的回复数据结构。这是将到达 SpAggregateReply1 节点的回复
结构。
实例 14-9 子流的回复数据结构
<solarsys-statement-reply>
<name>None</name>
<balance>20000</balance>
</solarsys-statement-reply>
输入终端:VIEW STMNT CMD IN
输入终端是被动的,而且只把输入消息发送到 out 终端,在此案例中,消息到达 MAP TO
STMNT REQUEST 计算节点。
计算节点:MAP TO STMNT REQUEST
计算节点获取输入 XML 消息,并将其重构为仅包含后端应用程序所需的信息。格式仍
然是 XML。
260
图 14-21 计算节点:MAP TO STMNT REQUEST
实例 14-10 中显示的代码是必需的,因为我们正在处理 JMS。我们允许有 MQRFH2 标
题——如果这些标题存在的理由是,接收此消息的 JMS 应用程序将使用 MQSeries 标题
上面的 MQRFH2 标题中的值。我们可以把 OutputRoot.MQRFH2.jms.Rto 设置为正确的
JMS 值,或者就
像在此案例中一样,将其设置为空值,这样就可以使用 MQSeries 标题值。
实例 14-10 计算节点:MAP STMNT TO REQUEST
IF OutputRoot.MQRFH2 IS NOT NULL THEN
SET OutputRoot.MQRFH2.jms.Rto =NULL;
END IF;
实例 14-11 显示计算节点的 ESQL 的剩余部分。此代码只是把输入值映射到输出值。
实例 14-11 计算节点:MAP STMNT TO REQUEST
SET OutputRoot.XML."solarsys-statement-request"."name"=
InputRoot.XML."view-statement-command"."user"."station";
SET OutputRoot.XML."solarsys-statement-request"."role"=
261
InputRoot.XML."view-statement-command"."user"."role";
输出终端:STMNT REQUEST OUT
输出终端也是被动的。它只是把消息发送到流中的下一个节点。由于这是子流,因而把
消息发送到使用该子流的消息流中的下一个节点。
14.3.3 MAP_RATECALC_CICS_REQUEST子流
MAP_RATECALC_CICS_REQUEST 子流把输入 XML 消息转换为 CICS 所要求的格
式。为了把消息转换为聚集器原始格式所需的信息,该子流将存储消息流处理回复所需
的信息(请参阅第 269 页的 14.3.4“PROCESS_RATECALC_CICS_ACK”)。图 14
-22 显示完整的子流。
图 14-22 MAP_RATECALC_CICS_REQUEST
该可复用的消息流将:
把信息保存到数据库 BK1UTIL 的表 RATECALCMSGLOG 中。此信息将用来处理
来自 CICS 应用程序的回复消息,并为 SpAggregateReply 节点生成回复消息。
把输入数据映射到 CICS 应用程序格式。
输入数据结构:视图-语句-命令
输入数据结构与第 248 页的“输入数据结构:视图-语句-命令”中讲述的输入数据结
构相同。
262
输出数据结构:IntCalcRequest
我们使用 MQSeries CICS 桥触发 C/C++ CICS 事务处理,该事务处理将根据请求中指
定的信用等级、类型和持续时间计算个性化利率。CICS 事务处理返回同样的结构,只
是已计算了利率。
I 在此消息流中,我们从 XML 转换为 CICS 应用程序格式。为此,我们将创建消息组和
消息,以便定义 CICS 输入结构。
实例 14-12 显示 CICS 事务处理所需的 C/C++数据结构。
实例 14-12 Ratecacl 请求结构
struct credit_rate {
char programName(8);
char type(8);
int year;
int duration;
int credit_rating;
int rate;
}
在图 14-23 中,您可以看到我们定义了名为 RateCalc_CICS 的消息集,而且在消息集
内,定义了 IntCalcRequest 消息以描述使用 Custom Wire Format 的消息格式。有关如何
使用消息集的详细信息,请参阅第 102 页的 7.2.1“消息类型”。
263
图 14-23 IntCalcRequest 消息细节
输入终端:RATECALC IN
此节点是被动的,而且只是把传入的消息发送到其 out 终端,我们已经把 out 终端连接
到“保存消息标题”(Save Message Header)数据库节点。
数据库节点:Save Message Header
此节点插入 PROCESS_RATECALC_ACK 消息流所需的信息,以恢复消息标题。由于
必须把消息格式更改为 MRM,而且必须更改某些 MQMD 值,所以我们需要把这些值
记录到 BK1UTIL 数据库。这使 PROCESS_RATECALC_ACK 消息流可以采用
SpAggregateReply 节点接收回复消息所需的方法,把这些值重新集合起来。
图 14-24 显示节点的配置。
264
图 14-24 数据库节点:保存消息标题
实例 14-13 显示图 14-24 的完整 ESQL。INSERT 语句将把 MQMD 值放到数据库表中,
以便将来引用。
实例 14-13 数据库节点:保存消息标题
INSERT INTO Database.RATECALCMSGLOG(
MQMD_MSGID,
MQMD_CORRELID,
MQMD_REPLYTOQ,
MQMD_REPLYTOQMGR,
MQMD_USERID,
MQMD_FORMAT,
MQMD_ENCODING,
MQMD_CODEDCHARSETID,
MQMD_VERSION,
MQMD_FEEDBACK,
MQMD_REPORT,
MQMD_MSGTYPE)
VALUES(
CAST(Root.MQMD.MsgId AS CHARACTER),
CAST(Root.MQMD.CorrelId AS CHARACTER),
Root.MQMD.ReplyToQ,
Root.MQMD.ReplyToQMgr,
Root.MQMD.UserIdentifier,
Root.MQMD.Format,
Root.MQMD.Encoding,
Root.MQMD.CodedCharSetId,
Root.MQMD.Version,
265
Root.MQMD.Feedback,
Root.MQMD.Report,
Root.MQMD.MsgType);
此节点的 out 终端连接到 Map to CICS Format 计算节点。
计算节点:Map to CICS format
此节点执行从 XML 格式到 CICS 所需格式的消息转换。图 14-25 显示节点的属性。
图 14-25 计算节点:Map to CICS format
让我们仔细看看此节点中使用的 ESQL 以及此节点如何执行转换。
实例 14-14 显示由于选中“复制消息标题”单选按钮而插入的代码。
266
实例 14-14 复制消息标题
DECLARE C INTEGER;
SET C =CARDINALITY(InputRoot.*[]);
DECLARE I INTEGER;
SET I =1;
WHILE I <C DO
SET OutputRoot.*[I]=InputRoot.*[I];
SET I=I+1;
END WHILE;
实例 14-15 显示由于在图 14-25 的“输出消息”段中选中“用作消息正文”(Use as
message body)复选框时生成的代码。
实例 14-15 用作消息正文
SET OutputRoot.Properties.MessageSet ='DNEFDMS072001';
SET OutputRoot.Properties.MessageType ='IntCalcRequest';
--Enter SQL below this line.SQL above this line might be regenerated,
causing any modifications to be lost. (在此行下键入SQL,SQL可能在此行上重新生成,从而导致
丢失任意更改)
提示:最好把此代码放在注释 --Enter SQL below this line……的上方。建议您遵循此
提议,并且不要把任何其他内容放在此注释上方。
在实例 14-16 中,我们删除了 CICS 桥的 MQRFH2 标题。同样地,之所以有此标题,
是因为我们的表示层(WebSphere)使用 JMS。
实例 14-16 删除 MQRFH2
--Need to remove the MQRFH2 header from the OutputRoot for CICS (需要从CICS的OutputRoot
将MQRFH2标题删除)
IF OutputRoot.MQRFH2 IS NOT NULL THEN
SET OutputRoot.MQRFH2 =NULL;
END IF;
实例 14-17 显示如何设置 OutputRoot MessageFormat 和 MessageDomain。我们还将设
置 MQMD 属性,以便进行 CICS 交换。
提示:我们更改了 MQMD ReplyToQ 和 ReplyToQMgr,这样,就可以由 PROCESS_
RATE_CALC_ACK 消息流处理该回复。此消息流将把其中消息按照 SpAggregateReply
节点所需要的方式组合在一起。
实例 14-17 设置属性和 MQMD
--Get Properties and MQMD set up (设置属性和MQMD)
SET OutputRoot.Properties.MessageFormat ='CWF';
SET OutputRoot.Properties.MessageDomain ='MRM';
SET OutputRoot.MQMD.ReplyToQ ='SOLARSYS.RATECALC.ACK';
267
SET
SET
SET
SET
SET
SET
OutputRoot.MQMD.ReplyToQmgr ='';
OutputRoot.MQMD.MsgType =MQMT_REQUEST;
OutputRoot.MQMD.Encoding =785;
OutputRoot.MQMD.CodedCharSetId =500;
OutputRoot.MQMD.Format =MQFMT_NONE;
OutputRoot.MQMD.CorrelId =MQCI_NEW_SESSION;
CICS 需要 MQMD 和 MRM 字段接受事务处理。通过把 CorrelId 设置为 MQCI_NEW
_SESSION,告知 CICS 桥此消息不是现有事务处理的组成部分。
实例 14-18 显示我们如何设置 MRM 输出数据。CICS 桥可以接受两种消息类型。第一
种消息类型以 CICS 桥标题开头。第二种类型(我们所用类型)没有此标题。因此,我
们需要提供 CICS 桥必须在消息的前 8 个字节中运行的程序。
实例 14-18 映射输出数据结构
--Set MRM Data structure up(设置MRM数据结构)
SET OutputRoot.MRM."Program"='RATECALC';
SET OutputRoot.MRM."Type"=
InputRoot.XML."view-statement-command"."interest-rate"."type";
SET OutputRoot.MRM."Year"=
CAST(TRIM(InputRoot.XML."view-statement-command"."interest-rate"."year")AS INTEGER);
IF InputRoot.XML."view-statement-command"."interest-rate"."duration"IS NULL THEN
SET OutputRoot.MRM."Duration"=48;--Hard code duration if missing ELSE(如果丢失ELSE
的硬代码)
SET OutputRoot.MRM."Duration"= CAST ( TRIM ( InputRoot.XML."view - statement -
command"."interest-rate"."duration")AS INTEGER);
END IF;
SET OutputRoot.MRM."CreditRate"=
CAST(TRIM(InputRoot.XML."view-statement-command"."interest-rate"."credit-rating ")AS
INTEGER);
SET OutputRoot.MRM."Rate"=0;--This gets calculated by the CICS transaction(这由CICS事务
处理进行计算)
输出终端:RATECALC REQUEST
此节点只把消息作为消息流中的“输出终端”进行传递。
268
14.3.4 PROCESS_RATECALC_CICS_ACK
我们前面提到过,需要把来自 CICS 的消息回复转换为 SpAggregateReply 节点所需要的
格式。进行此项工作所需的信息已由 MAP_RATECALC_CICS_REQUEST 子流存储
在 BK1UTIL 数据库中。在该子流中,ReplyToQ 设置为 SOLARSYS.RATECALC.ACK。
图 14-26 显示的 PROCESS_RATECALC_CICS_ACK 消息流从此回复队列中检索消
息,并且进行转换工作。
图 14-26 PROCESS_RATECALC_CICS_ACK 支持消息流
消息流的步骤如下:
从 SOLARSYS.RATECALC.ACK 队列获取 RATECALC CICS 回复消息。
从 BK1UTIL 数据库 RATECALCMSGLOG 表中检索以前存储的 MQMD 标题信
息。此信息存储在第 262 页的 14.3.3“MAP_RATECALC_CICS_REQUEST 子
流”中。
检查数据检索是否成功。如果不成功,检索工作就会扩展到
SOLARSYS.RATECALC.ACK.ERROR MQOutput 节点。
为 SpAggregateReply 节点把消息转换为 XML 格式。
269
输入数据结构
从 CICS 应用程序到此消息流的输入数据结构与第 262 页“MAP_RATECALC_CICS
_REQUEST 子流”的输出数据结构相同。
输出数据结构
此节点的输出数据结构与下面的实例相似:
实例 14-19 InterestReply 输出数据结构
<InterestReply>
<Type>NEW</Type>
<Year>2000</Year>
<Duration>48</Duration>
<Rate>673</Rate>
</InterestReply>
这就是到达 SpAggregateReply 节点的消息的格式,该消息将同来自后端请求的其他回复
聚集在一起。
MQInput 节点:SOLARSYS.RATECALC.ACK
图 14 - 27 显 示 此 MQInput 节 点 的 基 本 属 性 。 该 节 点 将 检 索 来 自
SOLARSYS.RATECALC.ACK 队列的消息。
图 14-27 MQInput 节点:SOLARSYS.RATECALC.ACK
图 14-28 显示默认属性。注意:我们将使用以前创建的消息集分析传入的消息。
270
图 14-28 默认属性
计算节点:恢复 MQMD 并映射到输出
此节点将执行消息转换。图 14-29 显示基本的节点属性。
271
图 14-29 恢复 MQMD 并映射到输出计算节点
在输入窗口中,您可以看到 BK1UTIL 数据库以及保存以前存储的 MQMD 值的
RATECALCMSGLOG 表。此节点将检索那些值,以便重新生成消息。
首先,要选中“复制消息标题”单选按钮。这样就插入 ESQL,以便执行复制功能。此
代码与图 14-14 中显示的代码相同。
接下来,我们需要访问 RATECALCMSGLOG 数据库表,以便检索存储的 MQMD 数据。
此数据将分配给称为 RATECALC.LOG 的临时 XML 文件夹。然后,使用此文件夹中存
储的数据重新生成消息标题。
实例 14-20 检索存储的 MQMD 数据
--Select the saved message header and store it temporarily in an XML folder
(选中存储的消息标题,并将其临时存储在XML文件夹)
272
SET OutputRoot.XML."RATECALC"."LOG"=THE (
SELECT L.MQMD_MSGID,L.MQMD_CORRELID,L.MQMD_REPLYTOQ,L.MQMD_REPLYTOQMGR,
L.MQMD_USERID,L.MQMD_FORMAT,L.MQMD_ENCODING,L.MQMD_CODEDCHARSETID,
L.MQMD_VERSION,L.MQMD_FEEDBACK,L.MQMD_REPORT,L.MQMD_MSGTYPE
FROM Database.RATECALCMSGLOG AS L
WHERE L.MQMD_MSGID =CAST(InputRoot.MQMD.CorrelId AS CHAR));
--Restore message header(存储消息标题)
SET OutputRoot.MQMD.MsgId =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_MSGID"AS BLOB);
--Standard MQ request reply,place msgid of original request in correlation id
(使MQ请求回复标准化,并将原始请求msgid放置在关联id)
SET OutputRoot.MQMD.CorrelId =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_MSGID"AS BLOB);
SET OutputRoot.MQMD.ReplyToQ =TRIM(TRAILING ''from
OutputRoot.XML."RATECALC"."LOG"."MQMD_REPLYTOQ");
SET OutputRoot.MQMD.ReplyToQMgr =TRIM(TRAILING ''from
OutputRoot.XML."RATECALC"."LOG"."MQMD_REPLYTOQMGR");
SET OutputRoot.MQMD.UserIdentifier =TRIM(TRAILING ''from
OutputRoot.XML."RATECALC"."LOG"."MQMD_USERID");
SET OutputRoot.MQMD.Format =TRIM(TRAILING ''from
OutputRoot.XML."RATECALC"."LOG"."MQMD_FORMAT");
SET OutputRoot.MQMD.Encoding =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_ENCODING"AS
INTEGER);
SET OutputRoot.MQMD.CodedCharSetId =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_CODEDCHARSETID"
AS INTEGER);
SET OutputRoot.MQMD.Version =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_VERSION"AS INTEGER);
SET OutputRoot.MQMD.Feedback =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_FEEDBACK"AS
INTEGER);
SET OutputRoot.MQMD.Report =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_REPORT"AS INTEGER);
SET OutputRoot.MQMD.MsgType =CAST(OutputRoot.XML."RATECALC"."LOG"."MQMD_MSGTYPE"AS INTEGER);
通过将使用 MRM 中定义的格式的输入数据映射到 XML 格式的输出, 实例 14-21 中
的代码可建造新的输出消息。
实例 14-21 把输入结构映射到输出结构
--setup InterestReply XML structure(设置InterestReply XML结构)
SET OutputRoot.XML."InterestReply"."Type"=InputRoot.MRM."Type";
SET OutputRoot.XML."InterestReply"."Year"=InputRoot.MRM."Year";
SET OutputRoot.XML."InterestReply"."Duration"=InputRoot.MRM."Duration";
SET OutputRoot.XML."InterestReply"."CreditRating"=InputRoot.MRM."CreditRating";
SET OutputRoot.XML."InterestReply"."Rate"=InputRoot.MRM."Rate";
SET OutputRoot.XML."InterestReply"."Rate"=TRIM(TRAILING '0'FROM (TRIM(LEADING '0'FROM
CAST((InputRoot.MRM."Rate"/100.0)AS CHARACTER))));
实例 14-22 中的代码删除用来保存 14-20 实例中选择语句的临时文件夹。不删除此临
时文件夹就会导致消息流因 XML 错误而失败。
273
实例 14-22 删除临时的 XML 文件夹
/*
You can only have one primary XML root so remove the temporary tree that used to store the select
that restored the message header (你只需拥有一个XML主根即可删除用于存储选择的临时树,而该
选择又可恢复消息标题)
*/
SET OutputRoot.XML."RATECALC"=NULL;
在实例 14-23 中,我们将消息属性更改为适当类型。
实例 14-23 设置属性
--change format of message to XML (将消息格式更改为XML)
SET OutputRoot.Properties.MessageSet ='';
SET OutputRoot.Properties.MessageType ='';
SET OutputRoot.Properties.MessageFormat ='';
SET OutputRoot.Properties.MessageDomain ='XML';
实例 14-24 显示如何为 MQOutput 节点确定目标列表。由于我们恢复了 MQMD
ReplyToQ,该队列应该是在 SpAggregateReply1 节点的 ReplyToQueue 属性中指定的队
列(请参阅第 251 页的“SpAggregateReply 节点:SpAggregateReply1”)。
实例 14-24 设置 DestinationList
--Map Destination List for the reply message to go to(将用于回复消息的目标列表映射以继续)
/*
if this is going thru the aggregate node this will be the aggregate q if not then it should be
the replytoq for the requesting application.Note the OutputRoot.MQMD was restored from the
saved msg header from the request.(如果通过聚集节点,这将是聚集q;否则将是请求应用程序的replytoq。注意:
OutputRoot.MQMD存储在来自请求存储的msg标题)
*/
SET OutputDestinationList.Destination.MQDestinationList.DestinationData.queueName =
OutputRoot.MQMD.ReplyToQ;
在此节点的“高级”选项卡中,我们选择“目标和消息”,这样就可以确保 MQOutput
节点把消息发送到正确的队列。
图 14-30 存储 MQMD 并映射到输出高级选项卡
274
过滤器节点:是否匹配?
这是一个简单的过滤器节点:如果 MQMD CorrelId 中不包含值,则数据库检索就会失
败,因为数据库不允许此值为“空”。如果测试为“假”或“未知”,消息则到达
SOLARSYS.RATECALC.ACK.ERROR 队列,以便进一步的检查。
图 14-31 过滤器节点是否匹配?
如果消息为“真”,就把消息传播到“清除消息日志数据库”节点。
数据库节点:清除消息日志
由于数据检索成功,因而我们不再需要在 BK1UTIL 表中存储临时消息标题数据。此节
点使用 ESQL 从数据库表中删除行。图 14-32 显示此数据库节点的配置窗口。
275
图 14-32:清除消息日志数据库节点
MQOutput 节点:RATECALC.OUT
图 14-33 显示此 MQOutput 节点的基本属性。我们不需要指定队列管理器名称或队列
名称,因为我们使用包含这些值的目标列表。
图 14-33 MQOutput 节点 RATECALC.OUT 的基本属性
图 14-34 显示“高级”属性。我们在这里指定使用的目标列表。
276
图 14-34 MQOutput 节点 RATECALC.OUT 的高级属性
MQOutput 节点:Interest Rate ACK ERROR!
图 14-35 显示错误队列所需的属性。消息发送到此队列,以防从前的节点中发生错误。
图 14-35 MQOutput 节点 Interest Rate ACK ERROR!
14.4 利用JMS消息
开发消息流时,了解将从何处接收消息以及消息的具体类型为何是非常重要。在消息来
自使用 JMS 的应用程序的情况下,有一些需要处理的额外消息标题。MQSI 开发人员必
需明白,MQRFH2 标题是随来自 JMS 的消息一起传送的。MQSI 将映射此数据,这样
就可以将其属性作为任何其他 InputRoot 和 OutputRoot 属性进行访问。
277
当把消息从 JMS 应用程序放到队列上时,数据部分将有一个加在数据前面的 MQRFH2
标题。消息流将携带此标题通过每个节点。您可以通过计算节点来对此进行控制。
当消息经过 MQInput 节点时,MQSI 为您把此标题分离到 InputRoot.MQRFH2 段中。
实例 14-36 显示加在消息数据段前面的 MQRFH2 标题。MQMD.Format 属性将设置为
MQRFH2。这恰好是位于 SOLARSYS.STATEMENT.REQUEST 队列的 MQRFH2 标题的
实例。
实例 14-25 MQRFH2 标题实例
<mcd><Msd>jms_text</Msd></mcd>
<jms>
<Dst>queue:///SOLARSYS.STATEMENT.REQUEST</Dst>
<Rto>queue://SOLARSYS.QM.AS/SOLARSYS.STATEMENT.REPLY</Rto>
<Cid>AMQblablabla</Cid>
</jms>
实例 14-26 显示如何使用 ESQL 在计算节点中引用这些值。
实例 14-26 从 ESQL 引用标题值
SET OutputRoot.MQRFH2.jms.Rto=NULL;
只是向您证明,我们浏览过来自后端 JMS 应用程序的回复消息的消息数据。图 14-36
中的数据段包括位于实际消息数据前面的 MQRFH2 数据。必须在应用程序中对此进行
解释(在格式方面)。利用 MQSI,您可以引用这部分消息(如实例 14-26)所示。
278
图 14-36 来自 JMS 应用程序的消息数据实例
在我们的实例应用程序中,在处理 MQRFH2 JMS 消息标题时需要考虑三方面的情况。
这在讲述消息流时已经提到过,我们只是再概括一下:
1.
在第 255 页的“计算节点:更改 MQMD 格式”中,我们把 MQMD.Format 属性设
置为字符串,因为聚集器只从原始请求恢复格式,如第 256 页的实例 14-6 所示。
另一种方法是在 OutputRoot 上生成 MQRFH2 标题。
2.
在第 260 页的“计算节点:MAP TO STMNT REQUEST”中,我们把传入的请求
映射到要发送到后端 WebSphere 应用程序的请求。这些应用程序碰巧使用 JMS。
JMS 将解释 MQRFH2 格式和标题数据,并首先应用到所有 MQSeries MQMD 标题
信息上。在我们的实例中,这意味着即使 SpAggregateCreate 节点用适当值取代
MQMD.ReplyToQ 和 MQMD.ReplyToQMgr,回复还是会返回到 SpAggregateReply
节点,而不是返回到 MQRFH2 JMS 标题。
279
我们把 MQRFH2.jms.Rto 设置为 NULL,这样接收 JMS 应用程序时就会使用 MQMD 值
确定把回复发送到什么地方。这么做就能确保回复流返回到 SpAggregateReply 节点,以
便在被发送到原始 ReplyToQ 之前能与其他响应聚集。如第 261 页的实例 14-10 所示。
3.
在第 266 页的“计算节点:映射到 CICS 格式”,我们把 MQRFH2 设置为非空值,
这样 MQSeries CICS 桥就不会误解我们所发送的内容。如第 267 页的实例 14-16
所示。
另一种方法是删除 MQRFH2 标题,并把消息格式设置在消息流的最开始。换句话说,
可在 SpAggregateCreate 节点前删除它,因为这是存储此类信息的地方。我们不去试验
这一操作,而是看看把此类信息放在什么地方最好。
14.5 准备测试应用程序
为了测试应用程序,您需要准备好并定义消息流需要的对象。
首先您需要在应用程序涉及的系统上安装和配置 MQSeries。在安装 MQSeries 需要后,
还需要定义队列以及应用程序所需的系统之间的连接。第 181 页的第 10 章“MQSeries 配
置”已讲述 MQSeries 的完整安装和设置。当完成此项任务后,就有了这些消息流所需
的所有 MQSeries 对象。
接下来,需要确保 MQSI 代理和配置管理器已经到位,而且可以正常工作。第 201 页的
第 11 章“MQSI 配置”已经讲述所需的 MQSI 安装过程。我们假定学习本章之前已经
完成这些步骤。
您需要做的最后一件事情就是确保应用程序所需的数据库到位。
14.5.1 创建数据库
实例应用程序使用四个应用程序数据库。其中两个数据库由代理独自访问,因此,它们
驻留在代理系统上:
TRANSLOG
BK1UTIL
280
另外两个数据库由多个应用程序使用,并驻留在后端 WebSphere Application Server 设备
上:
GUILD
STATIONS
在我们的实例中,消息流不通过架构名称引用 BK1UTIL 和 TRANSLOG 数据库。也就
是说,代理会附加用户 ID,该代理将根据该用户 ID 作为架构运行。在我们的实例中,
此架构/用户 ID 将是 Windows 代理上的 WASADMIN。对于 AIX 安装,MQSI 作为用
户 ID 使用 MQM 运行,因此,在下面的实例中,我们将用 MQM 替换 WASADMIN 的
所有实例。
您还需要确保 MQSI 用户 ID 具有更新这些数据库的授权。
创建 BKUTIL 应用程序数据库
BKUTIL 数据库和 RATECALCMSGLOG 表在第 262 页的 14.3.3“MAP_RATECALC_
CICS_REQUEST 子流”和第 269 页的“PROCESS_RATECALC_CICS_ACK”中使
用。实例 14-27 中显示的 DB2 语句用来创建该数据库。
实例 14-27 创建 BKUTIL 数据库
DB2 CREATE DATABASE BK1UTIL
DB2 CONNECT TO BK1UTIL
DB2 CREATE TABLE "WASADMIN"."RATECALCMSGLOG"(
"PKEY"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH +0, INCREMENT BY +1 ,NO CACHE ),
"MQMD_MSGID"VARCHAR(51),
"MQMD_CORRELID"VARCHAR(51)NOT NULL ,
"MQMD_REPLYTOQ"VARCHAR(48)NOT NULL ,
"MQMD_REPLYTOQMGR"VARCHAR(48)NOT NULL ,
"MQMD_USERID"VARCHAR(12)NOT NULL,
"MQMD_FORMAT"CHAR(8)NOT NULL ,
"MQMD_ENCODING"INTEGER NOT NULL ,
"MQMD_CODEDCHARSETID"INTEGER NOT NULL ,
"MQMD_VERSION"INTEGER NOT NULL ,
"MQMD_FEEDBACK"INTEGER NOT NULL ,
"MQMD_REPORT"INTEGER NOT NULL ,
"MQMD_MSGTYPE"INTEGER NOT NULL ,
"CREATED"TIMESTAMP NOT NULL WITH DEFAULT CURRENT TIMESTAMP)
IN "USERSPACE1";
--DDL Statements for indexes on Table "WASADMIN"."RATECALCMSGLOG"
(用于表"WASADMIN"."RATECALCMSGLOG"上索引的DDL语句)
DB2 CREATE INDEX "WASADMIN"."IDX_MQMD_MSGID"ON "WASADMIN"."RATECALCMSGLOG"
("MQMD_MSGID"ASC)
PCTFREE 10 ;
281
- - DDL Statements for primary key on Table "WASADMIN"."RATECALCMSGLOG" ( 句 用 于 表
"WASADMIN"."RATECALCMSGLOG"上主键的DDL语)
DB2 ALTER TABLE "WASADMIN"."RATECALCMSGLOG" ADD CONSTRAINT "PK_RATECALCMSGLOG"PRIMARY KEY
("PKEY");
创建 TRANSLOG 数据库
此数据库在第 239 页的 14.2“创建 SOLARSYS_TRANSLOG 消息流”中使用。实例 14
-28 中显示的 DB2 命令用来创建该数据库。
实例 14-28 创建 TRANSLOG 数据库
DB2 CREATE DATABASE TRANSLOG
DB2 CONNECT TO TRANSLOG
DB2 CREATE TABLE "WASADMIN"."LOGENTRIES"(
"UNIQUEIFER"BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY
(START WITH +0 ,INCREMENT BY +1 ,NO CACHE ),
"TRANSDATE"TIMESTAMP NOT NULL ,
"STATION"CHAR(10)NOT NULL ,
"AMOUNT"INTEGER NOT NULL ,
"NEWBALANCE"INTEGER NOT NULL ,
"CREATED"TIMESTAMP NOT NULL WITH DEFAULT CURRENT TIMESTAMP)
IN "USERSPACE1";
--DDL Statements for indexes on Table "WASADMIN"."LOGENTRIES"
(用于表"WASADMIN"."LOGENTRIES"上索引的DDL语句)
DB2 CREATE INDEX "WASADMIN"."TRANSDT_STATION"ON "WASADMIN"."LOGENTRIES"
("TRANSDATE"DESC,
"STATION"ASC)
PCTFREE 10 ;
--DDL Statements for primary key on Table "WASADMIN"."LOGENTRIES"
(用于表"WASADMIN"."LOGENTRIES"上主键的DDL语句)
DB2 ALTER TABLE "WASADMIN"."LOGENTRIES"
ADD PRIMARY KEY
("UNIQUEIFER");
创建 STATIONS 和 GUILD 数据库
STATIONS 和 GUILD 数据库是 WebSphere 企业 Beans(传输或显示资金)和 MQSI(在
语句中确定资金余额)使用的应用程序数据库。这些数据库位于后端应用程序服务器。
实例 14-29 显示用来创建 STATIONS 数据库和 STATIONSFUNDING 表的 DB2 命令。
282
实例 14-29 创建 STATIONS 数据库
DB2 CREATE DATABASE STATIONS
DB2 CONNECT TO STATIONS
DB2 CREATE TABLE "PDK "."STATIONSFUNDING"(
"NAME"VARCHAR(20)NOT NULL ,
"BALANCE"INTEGER NOT NULL WITH DEFAULT 0)
IN "USERSPACE1";
--DDL Statements for primary key on Table "PDK "."STATIONSFUNDING"
(用于表"PDK "."STATIONSFUNDING"上主键的DDL语句)
DB2 ALTER TABLE "PDK "."STATIONSFUNDING"
ADD PRIMARY KEY
("NAME");
实例 14-30 显示用于创建 GUILD 数据库和 GUILDACCOUNTS 表的 DB2 命令
实例 14-30 创建 GUILD 数据库
DB2 CREATE DATABASE GUILD
DB2 CONNECT TO GUILD
DB2 CREATE TABLE "PDK "."GUILDACCOUNTS"(
"ACCOUNT"VARCHAR(20)NOT NULL ,
"BALANCE"INTEGER NOT NULL WITH DEFAULT 0)
IN "USERSPACE1";
--DDL Statements for primary key on Table "PDK "."GUILDACCOUNTS"
(用于表"PDK "."GUILDACCOUNTS"上主键的DDL语句)
DB2 ALTER TABLE "PDK "."GUILDACCOUNTS"
ADD PRIMARY KEY
("ACCOUNT");
14.5.2 把数据库定义到代理
我 们 使 用 的 应 用 程 序 将 与 四 个 数 据 库 相 互 作 用 。 BK1UTIL 数 据 库 包 含
RATECALCMSGLOG 表,而且 TRANSLOG 数据库具有每项交易的 LOGENTRIES 项。
两个数据库都位于代理系统。两个 Guild 数据库(GUILD 和 STATIONS)都位于数据库
服务器。
在上述四种情况下,需要把数据库定义到代理系统并且注册到 ODBC。由于 TRANSLOG
和 BK1UTIL 数据库位于本地代理,因而您只需要把它们定义到 ODBC。由于 GUILD
和 STATIONS 数据库处于远程位置,您需要先把它们定义到代理系统上的 DB2 实例,
再把它们定义到 ODBC。
283
在 Windows 上定义远程数据库
在 Windows 上,执行此操作最容易的方法就是使用 DB2 代理配置助手实用工具。您可
以通过选择“开始”—>“程序”—>“IBM DB2”—>“代理配置助手”来启动此工具。
启动此工具后:
请单击“添加”;
选择“手动配置数据库链接”,并单击“下一步”;
选择 TCP/IP 协议,并单击“下一步”;
对于主机名称,输入数据库所在设备的主机名称或 IP 地址。我们使用了 DB2 的标
准端口号。单击“下一步”;
输入数据库名称。对于别名,输入将在 ODBC 驱动程序下注册的名称。在我们的
实例中,该名称与数据库名称(默认值)相同。单击“下一步”;
接受默认值,并单击“完成”;
当有提示时,请测试连接。如果测试成功,就表明已经定义数据库,并且已成功
添加 ODBC 连接。
把数据库添加到 Windows 上的 ODBC 配置
当您使用客户端配置助手把远程数据库定义到本地 DB2 时,您也可以选择把远程数据
库同时注册到 ODBC。对于本地数据库,您则需要使用客户端配置助手单独进行。
在启动“客户端配置助手”之后,您将会看到本地和远程数据库列表。请选择您要在
ODBC 中注册的数据库,并单击“属性”。确保选中“把此数据库注册到 ODBC”,并
且是“作为系统数据源”。单击“确定”。这样,就已把此数据库注册到了 ODBC。
您可能需要针对每个数据库重复这些步骤。
在 AIX 上定义远程数据库
在 AIX 上,您不能选择在本地使用“客户端配置助手”。要定义远程数据库,您需要使
用 DB2 命令。
下面显示的第一个命令将创建名为 tcpnode 的 DB2 目录项,以便在主机 rs627002 上定
义远程 DB2 实例。然后,下一个命令把 GUILD 数据库添加到 DB2 目录,并将其位置
定义为 tcpnode (rs627002)定义的系统。
db2 catalog tcpip node tcpnode remote rs627002 server db2cdb2inst1
db2 catalog db GUILD as GUILD at node tcpnode
284
把数据库添加到 AIX 上的 ODBC 配置
在 AIX 上,要添加代理数据库,需要更新/var/mqm/odbc 目录中的.odbc.ini 文件(隐
藏文件)。实例 14-31 显示我们为了注册 AIX 代理数据库 MQSIBK2 而添加到.odbc.ini
的语句。
实例 14-31 增加代理数据库到 ODBC 配置
[ODBC Data Sources]
MQSIBK2=IBM DB2 ODBC Driver
[MQSIBK2]
Driver=/home/db2inst1/sqllib/lib/db2.o
Description=MQSIBK2 DB2 ODBC Database
Database=MQSIBK2
[TRANSLOG]
Driver=/home/db2inst1/sqllib/lib/db2.o
Description=TRANSLOG DB2 ODBC Database
Database=TRANSLOG
[BK1UTIL]
Driver=/home/db2inst1/sqllib/lib/db2.o
Description=BK1UTIL DB2 ODBC Database
Database=BK1UTIL
14.6 测试应用程序
在让其他人参与之前,最好对尽可能多的消息流进行测试。下面有一些需要考虑的提示
和注意事项:
如果消息流输入为 XML 文件,那么请把测试 XML 流存储在文本文件中。这样,
就可以很容易地把文本剪贴到测试消息中;
测试错误逻辑。如果在输入队列上定义有取消队列和取消阈值,请进行一次消息
失败试验,以确保这些阈值有效;
确保获得预期结果;
在测试时,请检查死信队列,以便查明丢失的消息;
观察“Windows 事件查看器”,看是否有特定的 MQSI 错误和消息流错误。通常
有足够的信息帮助您查明问题。在 AIX 上,您可以在 syslog.user 中找到消息。此
文件通常是在安装过程中创建的。
285
提示:如果您已为用户消息配置了 syslog,请执行以下命令:
cd /var
mkdir log
touch /var/log/syslog.user
chown root:mqbrkrs /var/log/syslog.user
chmod 750 /var/log/syslog.user
把以下行添加到/etc/syslog.conf:
user.error /var/log/syslog.user
消息级别(user.level) 的值可以是错误、信息、排除错误或警告。
14.6.1 测试消息
测试消息流的最佳办法很明显是通过消息流发送消息,并检查结果。您可以从 MQSeries
Explorer 进行此测试:
从 MQSeries Explorer 上,右键单击队列,并选择“放置测试消息”;
图 14-37 在 MQSeries 队列上放置测试消息
按照 MQSI 输入终端的要求准确输入消息内容。
<transactionrecord><station>JUPITER</station><amount>1600</amount><transdat e>20001-
11-09
23:00:51.123000</transdate></transactionrecord>
286
图 14-38 输入测试消息
检查所需结果。例如,如果您正在更新数据库,请使用 DB2 控制中心查看表的内
容,检查是否已添加数据。
MQSeries for Windows 提供有 API Exerciser 工具。此应用程序(amqapi)位于 MQSeries
Root\bin 目录下。在打开、获取和放置消息时,它赋予您完全 MQI 功能。
MQSeries 同时提供实例程序,可用来在队列上放置或获取数据。例如,您可以使用
amsqput 实例测试是否可以把消息放到队列上。这些实例将在“MQSeries 信息中心”中
详细讲述。
14.6.2 跟踪节点
调试消息流的另一个好方法是在消息流中使用跟踪节点。跟踪节点属于原始节点,并且
应将它们连接到要跟踪的节点的 failure 终端。
287
图 14-39 连接跟踪节点
每次输入与一个文本文件连接的跟踪节点(如图 14-40 所示)时,跟踪节点就在一个
文本文件中放置代码行 “****”,后面是请求的信息,再后面是另一行 “*****”。跟踪输
出则放置在 c:\mqsitrace.txt。
图 14-40 增加跟踪节点
288
提示:如果在跟踪文件中看不到任何内容,则很可能您从未来过该节点。
14.6.3 检查传入的消息
消息流有时遇到的问题可能是来自队列的,但不是您所期望的数据。要查看传入的数据,
请使用“MQSI 操作”选项卡停止消息流。这样,前往该消息流的消息就会停留在
MQSeries 队列上,而且您可以使用 MQSeries Explorer 在该处查看这些消息。
要使用 MQSeries Explorer 检查保留在队列上的消息,请突出显示该队列并双击。
图 14-41
双击消息,并选择“数据”选项卡。
289
图 14-42 在队列上引入消息
14.7 部署应用程序
创建消息流仅仅是第一个步骤。在使用消息流之前,必须将其应用到 MQSI 代理。
14.7.1 把代理定义到MQSI
告知配置管理器其代理所在的位置:
1.
在“控制中心”中,选择“拓扑”选项卡。在“域层次结构”窗格(最左边的窗
格)中,右键单击“拓扑”节点,并选择“签出”。
提示:如果遇到授权失败,请确保您所使用的用户 ID 属于代理域内所有系统上的
MQSI 安全组的成员。
“拓扑”节点旁边出现一个符号,则表明您已控制该节点。
290
图 14-43 定义代理到配置管理器
2.
现在,再次右键单击“拓扑”节点,并选择“创建”—>“代理”。在窗口中,输
入代理和队列管理器的名称。在我们的案例中,有一个名为 BROKER1 的代理,
它位于名为 SOLARSYS.QM.BROKER1 的队列管理器上。
3.
单击“完成”,并把代理定义到拓扑。新建代理就会显示在“拓扑”窗格中。
图 14-44 代理
14.7.2 创建MQSI执行组
接下来,我们将在代理中创建执行组,以便运行应用程序的消息流。
1.
单击“分配”选项卡。在“域层次结构”窗格(最左边的窗格)中,右键单击
“BROKER1”,并选择“创建”—>“执行组”。
291
2.
在显示的窗口中,输入新的执行组的名称(我们输入“Solar 系统执行组”)。
3.
单击“完成”。这样,新的执行组将显示在针对 BROKER1 的框中的“域拓扑”
面板中。
14.7.3 把消息流分配给执行组
接下来,我们必须给代理分配消息流。
1.
单击“分配”选项卡。在“可分配资源”窗格(中间的窗格)中,单击“消息流”
项旁边的“+” 符号,查看所有已定义的消息流。我们已经创建了整理消息流的
文件夹。
2.
在“域拓扑”窗格(最右边的窗格)中,您会看到我们所添加的代理 (BROKER1)
及其执行组。右键单击所需执行组,并选择“添加”—>“消息流”。
3.
在显示的窗格中,使用鼠标和 Ctrl 键在我们的应用程序中选择消息流,并单击“完
成”。
图 14-45 分配到 BROKER1 的消息流
14.7.4 保存配置并将其应用到代理
现在,我们已完成配置更改。但还有两项任务。首先,我们必须把配置保存到配置管理
器主持的共享存储库。然后,我们将把更改应用到代理域,这样,更改才可生效。
292
1.
从“文件”菜单上,选择“签入”—>“全部”(保存为共享)。这将获取仅存在
于工作区的所有组件,并检查共享配置的最新版本;
2.
从“文件”菜单上,选择“应用”—>“增量配置”(所有类型);
3.
显示窗口,并确认应用请求。这是 MQSI 异步操作所特有的。其意思是,已将请
求放到 MQSeries 队列上,而不是应用已经完成;
4.
当应用完成时,可在“日志”窗格中检查结果。返回消息可能需要几秒钟时间。
如果未显示消息,请单击“刷新”按钮。
图 14-46 显示域层次结构、可分配的资源以及域拓扑。域拓扑显示已分配给代理的消
息流和消息集。
图 14-46 域层次结构、可分配的资源以及域拓扑
293
294
第四部分
附录
295
296
A
源代码
本附录中包含曾在实例应用程序中使用的精选源代码。包含这些源代码的目的是将本书
与以前的论述联系起来。除本书中讨论的技术之外,我们并非想要通过这些源代码说明
编码方面的最佳做法。事实上,由于时间有限,有很多更好的方法未能在本书中介绍。
297
DisplayFundsRichResults JSP
实例:A-1 DisplayFundsRichResults.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<%@page session="false"info="Pattern Development"errorPage="WEB-INF/error.jsp"%>
<%@page import="com.ibm.pdk.*"%>
<!--This bean encapsulates the data from the backend -->(此bean可从后台压缩数据)
<jsp:useBean
id="guildViewBean"type="com.ibm.pdk.view.DisplayBalancesViewBean"scope="request"
/>
<HTML>
<HEAD>
<!--Store JavaScript functions in a separate file -->(在独立文件中存储JavaScript功
能)
<SCRIPT LANGUAGE="JavaScript"src="WEB-INF/script/displayBalance.js"></SCRIPT>
<META http-equiv="Content-Style-Type"content="text/css">
<TITLE>Guild of Weather Masters -Funding</TITLE>
<LINK href="WEB-INF/theme/Master.css"rel="stylesheet"type="text/css">
</HEAD>
<BODY BACKGROUND="WEB-INF/images/space.gif">
<%@include file="WEB-INF/header.jsp"%>
<CENTER>
<TABLE border="0">
<COL span="1"valign="top">
<TR>
<TD ALIGN="CENTER"WIDTH="900">
<TABLE CELLPADDING="1"BGCOLOR=""BORDER="0">
<%
//Create HTML for each station in the system(为系统中的所有站创建HTML)
int newRow =0;
//Enunerate through the result set ->(Enunerate通过结果设置)
while(guildViewBean.getResults().hasMoreElements()){
StationBalance sb =guildViewBean.getResults().next();
//newRow check ensures max 2 stations displayed per row ->(newRow检查可确保每行至多
显示两个站)
if(newRow %2 ==0){
%>
<TR BGCOLOR="">
<%
}
%>
<TD><DIV ALIGN="CENTER"><B><%=sb.getName()%></B><br><IMG
SRC="images/stations/<%=sb.getName()%>.gif">
<br><IMG SRC="WEB-INF/images/astro.gif"><%=sb.getBalance()%></DIV></TD>
<TD><br>
<%
//Add rich representation of stations wealth here ->(在此添加站的丰富表达方法)
//For efficieny create a temp variable ->(给efficieny创建临时变量)
int amount =sb.getBalance();
298
//Make cells width the same as the image width *max #of images possible +a buffer(使单元
宽度与图像宽度*max #of images possible +a buffer相同)
//35 is the size of the icon, formerly defined by the view bean, now the responsibility
(图标大小为35,以前由视图bean定义,)
//of the JSP writer(现在是JSP复写器来进行定义)
int cellWidth =(35 *guildViewBean.getEquipmentCosts().length)+35 ;
%>
<TABLE BORDER="0"CELLPADDING="0"BGCOLOR="">
<TR><TD VALIGN="TOP"HEIGHT="35"WIDTH="<%=java.lang.Integer.toString(cellWidth)%>">
<%
int noOfGraphics =guildViewBean.getNumberOfEquipmentItems(amount,guildViewBean.FIRST);
for(int j=0;j <noOfGraphics;j++){
%>
<IMG SRC="<%=guildViewBean.getEquipmentImages()[guildViewBean.FIRST]%>"width="35"
height="35">
<%
}
//If there are no images, we need to add non-breaking space to pad the cell ->(如果没
有图像,我们需要增加不间断空间来填充单元)
if(noOfGraphics ==0){
%>
&nbsp;
<%
}
%>
</TD></TR><TR><TD NOWRAP HEIGHT="35"WIDTH="<%=java.lang.Integer.toString(cellWidth)
%>">
<%
noOfGraphics =guildViewBean.getNumberOfEquipmentItems(amount,guildViewBean.SECOND);
for(int j=0;j <noOfGraphics;j++){
%>
<IMG SRC="<%=guildViewBean.getEquipmentImages()[guildViewBean.SECOND]%>"width="35"
height="35">
<%
}
//If there are no images, we need to add non-breaking space to pad the cell ->(如果没
有图像,我们需要增加不间断空间来填充单元)
if(noOfGraphics ==0){
%>
&nbsp;
<%
}
%>
</TD></TR><TR><TD NOWRAP HEIGHT="35"WIDTH="<%=java.lang.Integer.toString(cellWidth)%>">
<%
noOfGraphics =guildViewBean.getNumberOfEquipmentItems(amount,guildViewBean.THIRD);
for(int j=0;j <noOfGraphics;j++){
%>
<IMG SRC="<%=guildViewBean.getEquipmentImages()[guildViewBean.THIRD]%>"width="35"
height="35">
<%
299
}
//If there are no images, we need to add non-breaking space to pad the cell ->(如果没
有图像,我们需要增加不间断空间来填充单元)
if(noOfGraphics ==0){
%>
&nbsp;
<%
}
%>
</TD></TR><TR><TD NOWRAP HEIGHT="35"WIDTH="<%=cellWidth %>">
<%
noOfGraphics =guildViewBean.getNumberOfEquipmentItems(amount,guildViewBean.FOURTH);
for(int j=0;j <noOfGraphics;j++){
%>
<IMG SRC="<%=guildViewBean.getEquipmentImages()[guildViewBean.FOURTH]%>"width="35"
height="35">
<%
}
//If there are no images, we need to add non-breaking space to pad the cell ->(如果没
有图像,我们需要增加不间断空间来填充单元)
if(noOfGraphics ==0){
%>
&nbsp;
<%
}
%>
</TD></TR></TABLE>
</TD>
<%
if(newRow %2 ==1){
%>
</TR>
<%
}
newRow++;
}
%>
</TABLE>
<!--Rich HTML results END ->
<br>
<!--Try building the Legend in the JSP, not getting it from the view bean ->(尝试在JSP
中构建Legend,而不从视图bean中获取它)
<TABLE BGCOLOR=""BORDER="0">
<TR>
<%
for(int i =0;i <guildViewBean.getEquipmentCosts().length;i++){
%>
<TD><IMG SRC="<%=guildViewBean.getEquipmentImages()[i]%>"></TD><TD>=<IMG
SRC=WEB-INF/images/astro.gif><%=guildViewBean.getEquipmentCosts()[i]%></TD>
<%
}
300
%>
</TR>
</TABLE>
<!--DONE !->(完成)
</TD>
<TD ALIGN="CENTER"WIDTH="200">
<TABLE BORDER="1"BGCOLOR="DARKGRAY">
<TR>
<TD ALIGN="CENTER">
<IMG SRC="WEB-INF/images/smallshield.gif"><H3><IMG
SRC="WEB-INF/images/astro.gif"><%=guildViewBean.getGuildBalance()%></H3>
<!--THE TRANSFER FORM STARTS ->(转换格式开始)
<!--Try building the form in the JSP,not getting it from the view bean ->(尝试在JSP中
构建Legend,而不从视图bean中获取它)
<!--Main stations table ->(主站表格)
<table width="45"border="0"cellspacing="0"cellpadding="0"height="65">
<tr bgcolor="#808080">
<td><center><b><font size=2 color="#ffffff">Grant Astros</font></b></center></td><tr>
<td height="69">
<table width="100%"border="0"cellspacing="0"cellpadding="1">
<FORM METHOD=POST ACTION='/PDK/TransferFundsServlet'>
<tr bgcolor="#808080">
<td colspan="2"height="3"><font color="#ffffff"face="Default Font"
size="2">Amount</font><br>
<input type="text"name="AMOUNT"size="10"value=""maxlength=7>
</td>
</tr>
<tr bgcolor="#808080">
<td colspan="2"height="2"><font color="#ffffff"face="Default Font"
size="2">To</font>
<br>
<SELECT name="STATION"'Select weather station to receive grant';">
//Enunerate through the result set(Enunerate通过结果设置)
<%
while (guildViewBean.getResults().hasMoreElements()){
com.ibm.pdk.StationBalance sb =guildViewBean.getResults().next();
%>
<OPTION value="<%=sb.getName()%>"><%=sb.getName()%>
<%
}
%>
</SELECT>
</td>
</tr>
<tr bgcolor="#808080">
<td height="2"><center>
<input type="submit"onClick="return attemptSubmit();window.status ='Click
here to grant';"name="submit2"value="Transfer"></center>
301
</td></tr><tr bgcolor="#808080">
</tr>
</form>
</table>
</td>
</tr>
</table>
<!--THE TRANSFER FORM ENDS ->
</TD>
</TR>
</TABLE>
</TD>
<TD>&nbsp;</TD>
</TR>
</TABLE>
</CENTER>
<center><a href="/PDK/ViewStatementServlet">View Statement</a></center>
</BODY>
</HTML>
DisplayBalancesViewBean
实例:A-2 DisplayBalancesViewBean
package com.ibm.pdk.view;
import com.ibm.pdk.Logger;
import com.ibm.pdk.*;
/**
*Provides HTML representations of the result data for a(为DisplayAllResults请求提供结果)
*'DisplayAllResults' request.(数据的HTML表示法)
*
*This can be either in the form of a simple HTML table,(这可以是简单的HTML表格形式的
*a rich, graphical representation, or somewhere in between.(,也可以是丰富的图形表示法,或者居
其间)
*
*The bean also provides the HTML form needed to submit(该bean也可提供需要提交转换请求的HTML表
单)
*transfer requests, and the JavaScript used to validate this.(,并可使用JavaScript进行确认)
*
*Web developers creating JSPs can set properties on the bean(创建JSPWeb开发者能在bean上设置属性)
*to alter the appearance of the generated HTML, such as the(以改变所生成HTML的外观,诸如)
*color of the table, and the thickness of the table's border.(表格颜色,以及表格边界的厚度)
*
*@author: Steve Young
*/
public class DisplayBalancesViewBean {
//This custom object contains mappings of WeatherStations to their current balance(此定制对象包含
对当前平衡的WeatherStations映射)
302
private com.ibm.pdk.StationsBalanceList results;
//A String representation of the Guild's current balance
private String guildBalance;(Guild的当前字符串表示法可平衡专用字符串guildBalance)
/**
*An array storing the prices of Weather Reading(以费用为顺序存储大气探测)
*equipment, in order of expense(设备价格的数组)
*/
private int[]equipmentCosts ={200000,
40000,
8000,
1600};
/**
*Constant used in indexing the <code>equipmentImages</code>array of(在大气探测设备图标
索引<code>equipmentImages</code>数组中使用的常数)
nd
*weather equipment icons.1st represents the most expensive,2 (第一个常数表示最大金额)
*the second most expensive and so on.(第二个常数表示次昂贵的,以此类推。)
*/
public static final int FIRST =0;
//Index for the <code>equipmentImages</code>array -see @#FIRST
public static final int SECOND =1;
//Index for the <code>equipmentImages</code>array -see @#FIRST
public static final int THIRD =2;
//Index for the <code>equipmentImages</code>array -see @#FIRST
public static final int FOURTH =3;
//Index for the <code>equipmentImages</code>array -see @#FIRST
public static final int FIFTH =4;
/**
*Array of Strings used to define the graphic images that represent (用于定义可表示各种级
别大气探)
*the various levels of weather reading equipment.(测设置的图形图像的字符串数组)
*These may be altered by the web developer via accessor methods.(通过存取程序,web开发者
可更改这些)
*/
private String[]equipmentImages ={"images/equipment/satellite.gif",
"images/equipment/dish.gif",
"images/equipment/man.gif",
"images/equipment/balloon.gif",
//
"images/equipment/thermometer.gif"
};
//Integer defining the width and height of equipment icons(定义设备图标宽度及高度的
整数)
private int equipmentIconSize =35;
//DisplayBalancesViewBean constructor. (DisplayBalancesViewBean构造程序)
public DisplayBalancesViewBean(){
super();
}
303
public int[]getEquipmentCosts() {
return equipmentCosts;
}
public int getEquipmentIconSize() {
return equipmentIconSize;
}
public String[]getEquipmentImages() {
return equipmentImages;
}
public String getGuildBalance() {
return guildBalance;
}
/**
*This method uses the modulous function to calculate the number of (该方法使用系数功能来
计算一个)
*a particular piece of weather equipment a weather station could afford(气象站能购买的特
定气象设备的数量,)
*to buy, according to the following rule: (规则如下:)
*
*-a station would always buy as many of the most expensive item(-一个气象站总会购买其
能买得起的最)
*as it could afford.(多数量的最昂贵商品。)
*
*-with the remaining balance, the station would then buy as many of(利用剩下的余额,该
站然后会购买)
* the second most expensive item,and so on to the least expensive(最多数量次昂贵商品,)
* item. (对于最便宜的商品,可以此类推)
*
*The method caller passes the total balance the station has in the
*<code>totalBalance</code>parameter. The <code>equipmentIndex</code>
*parameter specifies the index to the graphicValues array, i.e. whether
*the calculations should be performed for the most expensive piece of
*equipment, or the 2nd most expensive, and so on.
*
(该方法调用者可传递该站在<code>totalBalance</code>参数中所拥有的差额总额。)
<code>equipmentIndex</code>参数可指定graphicValues组数的索引,例如,在最昂贵的与次昂贵,以
及其它设备之间,我们应对哪些进行计算。)
*@return int
*@param total int whichGraphic int
*/
public int getNumberOfEquipmentItems(int totalBalance,int equipmentIndex){
int equipmentValue =getEquipmentCosts()[equipmentIndex];
//Determine on which piece of equipment we are basing our calcuatlions,(决定以哪些
设备作为根据,以进行计算)
//as the most expensive (1st in the array)is a special case.(因为最昂贵<数组中的
第一个>的是一个特殊的情况)
//This is because we do not need to calculate the amount of money(因为我们不必计金额)
//that will have been used on more expensive items (as there are none!)(其将会用
于更为昂贵的商品<此处没有!>)
If(equipmentIndex >0){
int previousEquipmentValue =getEquipmentCosts()[equipmentIndex -1];
int previousRemainder =totalBalance %previousEquipmentValue;
totalBalance =previousRemainder;
}
304
int remainder =totalBalance %equipmentValue;
int freeToSpend =totalBalance -remainder ;
int piecesOfEquipment =freeToSpend /equipmentValue;
return piecesOfEquipment;
}
//Accessor methods.(存取程序方法。)
public com.ibm.pdk.StationsBalanceList getResults(){
return results;
}
public void setEquipmentCosts(int[]newEquipmentCosts){
equipmentCosts =newEquipmentCosts;
}
public void setEquipmentIconSize(int newEquipmentIconSize){
equipmentIconSize =newEquipmentIconSize;
}
public void setEquipmentImage(int index,String newEquipmentImage){
if(index <equipmentImages.length){
getEquipmentImages()[index]=newEquipmentImage;
}
}
public void setEquipmentImages(String[]newEquipmentImages){
equipmentImages =newEquipmentImages;
}
public void setGuildBalance(java.lang.String newGuildBalance){
guildBalance =newGuildBalance;
}
public void setResults(com.ibm.pdk.StationsBalanceList newResults){
results =newResults;
}
private void writeToLog(String message){
Logger.instance().write("[DisplayBalancesViewBean]-"+message);
}
}
ViewStatementServlet
实例:A-3 ViewStatementServlet
package com.ibm.pdk.servlet;
import
import
import
import
import
javax.servlet.http.*;
com.ibm.pdk.command.util.*;
com.ibm.pdk.*;
com.ibm.websphere.command.*;
com.ibm.pdk.command.*;
public class ViewStatementServlet extends HttpServlet
{
305
//Process incoming HTTP GET requests(处理进入的HTTP GET请求)
public void doGet(HttpServletRequest request,HttpServletResponse response)throws
javax.servlet.ServletException,java.io.IOException {
performTask(request,response);
}
//Process incoming HTTP POST requests(处理进入的HTTP POST请求)
public void doPost(HttpServletRequest request,HttpServletResponse response)throws
javax.servlet.ServletException,java.io.IOException {
performTask(request,response);
}
//Returns the servlet info string.(返回服务件信息字符串。)
public String getServletInfo(){
return super.getServletInfo();
}
//Initialize the servlet.(初始化服务件。)
public void init() {
}
//Process incoming requests for information(处理进入信息请求)
public void performTask(HttpServletRequest request,HttpServletResponse response) {
try{
//Get the user's identity(获得用户的身份)
String role =null;
String station =null;
if(request.isUserInRole("Administrator")){
writeToLog("User IS in Administrator Role");
role ="Administrator";
station ="All";
}else{
writeToLog("User IS NOT in Administrator Role");
role ="User";
station ="All";
}
//prepare command(准备命令)
ViewStatementCommand command =
(ViewStatementCommand)CommandFactory.instance().getCommand(CommandFactory.VIEW_STATEMENT
_COMMAN
D);
writeToLog("Obtained ViewStatementCommand");
command.setCommandTarget(CommandTargetFactory.instance().getCommandTarget
(CommandTargetFactory.
LOCAL_COMMAND_TARGET));
command.setUserRole(role );
command.setStation(station );
//set interest rate info (this should probably be put into a db somewhere)
(设置利息率信息<这应该能放置到数据库的某个地方>)
306
InterestRate rate =new InterestRate();
rate.setCreditRating(0 );
rate.setDuration(48 );
rate.setType("NEW");
rate.setYear("2001");
command.setInterestRate(rate );
//execute the command(执行该命令)
command.execute();
//send the results to the user(向用户发送结果)
writeToLog("Command executed.Putting results to UI");
response.getOutputStream().println(command.getMessage());
response.getOutputStream().flush();
}catch(Throwable theException){
//uncomment the following line when unexpected exceptions(当发生意外异常以解决调试问题时)
//are occuring to aid in debugging the problem.(不要对如下行进行评论。)
theException.printStackTrace();
}
}
private void writeToLog(String message){
com.ibm.pdk.Logger.instance().write("[ViewStatementServlet]-");
}
}
ViewStatementCommand
实例:A-4 ViewStatementCommand
package com.ibm.pdk.command;
import com.ibm.websphere.command.*;
import com.ibm.pdk.*;
public interface ViewStatementCommand extends TargetableCommand {
public StationsBalanceList getBalances();
public InterestRate getInterestRate();
String getMessage();
public String getStation();
public TransactionMap getTransactions();
public String getUserRole();
public void setBalances(StationsBalanceList balanceList);
public void setInterestRate(InterestRate rate);
307
public void setStation(String station);
public void setUserRole(String role);
}
ViewStatementCommandImpl
实例:A-5 ViewStatementCommandImpl
package com.ibm.pdk.command;
import com.ibm.websphere.command.*;
import javax.naming.*;
import javax.naming.directory.InitialDirContext;
import java.util.*;
import javax.jms.*;
import com.ibm.pdk.*;
import org.jdom.*;
import org.jdom.input.*;
public class ViewStatementCommandImpl extends TargetableCommandImpl implements
ViewStatementCommand {
private StationsBalanceList balances;
private com.ibm.pdk.InterestRate interestRate;
private java.lang.String station;
private java.lang.String userRole;
private com.ibm.pdk.TransactionMap transactions;
private java.lang.String message;
/**
*ViewStatementCommandImpl constructor comment.(ViewStatementCommandImpl 构造程序评论。)
*/
public ViewStatementCommandImpl(){
super();
}
public StationsBalanceList getBalances(){
return balances;(返回余额)
}
public com.ibm.pdk.InterestRate getInterestRate(){
return interestRate;
}
public java.lang.String getMessage(){
return message;(返回消息)
}
private String getRequestString(){
/*
*Build the following XML doc(构建如下XML文档)
*
*<view-statement-command>
308
*<user>
*
<station></station>(optional)
*
<role></role>
*</user>
*<interest-rate>
*
<type></type>
*
<year></year>
*
<credit-rating></credit-rating>
*
<duration></duration>
* </interest-rate>
*</view-statement-command>
*/
StringBuffer xml =new StringBuffer();
xml.append("<view-statement-command>");
xml.append("<user>");
if (getStation()!=null &&!(getStation().trim().equals(""))){
xml.append("<station>");
xml.append(getStation());
xml.append("</station>");
}
xml.append("<role>");
xml.append(getUserRole());
xml.append("</role>");
xml.append("</user>");
xml.append("<interest-rate>");
xml.append("<type>");
xml.append(getInterestRate().getType());
xml.append("</type>");
xml.append("<year>");
xml.append(getInterestRate().getYear());
xml.append("</year>");
xml.append("<credit-rating>");
xml.append(getInterestRate().getCreditRating());
xml.append("</credit-rating>");
xml.append("<duration>");
xml.append(getInterestRate().getDuration());
xml.append("</duration>");
xml.append("</interest-rate>");
xml.append("</view-statement-command>");
return xml.toString();
309
}
public java.lang.String getStation(){
return station;
}
public com.ibm.pdk.TransactionMap getTransactions(){
if(transactions ==null ){
setTransactions(new TransactionMap());
}
return transactions;(返回事务处理)
}
public java.lang.String getUserRole(){
return userRole;
}
public boolean isReadyToCallExecute(){
return true;
}
/**
*Sends an XML message to a queue and waits for a message to return containing
*the data to be displayed to the user.(向队列发送XML消息,并等待返回一个包含有要向用户显
示的数据。)
*/
public void performExecute()throws Exception {
writeToLog("[IN]-performExeccute()");
Hashtable environment =new Hashtable();
environment.put(Context.PROVIDER_URL,PdkProperties.singleton().getProviderURL());
environment.put(
Context.INITIAL_CONTEXT_FACTORY,
PdkProperties.singleton().getInitialContextFactoryClassName());
writeToLog("Getting inital context");
InitialDirContext context =null;
context =new InitialDirContext(environment);
String qcfJndiName =PdkProperties.singleton().getQueueConnectionFactoryJNDIName();
writeToLog("Getting QueueConnectionFactory from JNDI[name="+qcfJndiName +"]");
QueueConnectionFactory qConFactory =(QueueConnectionFactory)context.lookup(qcfJndiName);
writeToLog("Getting queue connection.");
QueueConnection qCon =qConFactory.createQueueConnection();
writeToLog("Getting queue session");
QueueSession qSession =qCon.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
String requestQName =PdkProperties.singleton().getRequestQueueJNDIName();
String replyQName =PdkProperties.singleton().getReplyQueueJNDIName();
writeToLog("Getting queues from JNDI namespace[requestJNDIName="
310
+requestQName +"][replyJNDIName="+replyQName +"]");
Queue requestQueue =(Queue)context.lookup(requestQName );
Queue replyQueue =(Queue)context.lookup(replyQName);
writeToLog("Building queue sender.");
QueueSender qSender =qSession.createSender(requestQueue);
//build the message(构建该消息)
TextMessage requestMessage =qSession.createTextMessage();
String message =getRequestString();
requestMessage.setText(message);
requestMessage.setJMSReplyTo(replyQueue);
requestMessage.setJMSCorrelationID("BobLuvsGoats");
writeToLog("Message built[xml="+message +"]");
//send the message(发送该消息)
qSender.send(requestMessage);
writeToLog("Message sent.");
//we expect the Correlation ID of the reply to match this mgs MessageID -so capture it
(//我们期望获得回复的Correlation ID以便匹配这一消息MessageID-对其进行捕获)
String messageId =requestMessage.getJMSMessageID();
writeToLog("Correlation ID of sent message ="+messageId);
//flip the connection to 'receive'mode
qCon.start();
/*
*Set up the queue receiver with a message selector to pull the request to
*this message(利用消息选择器建立队列接收者,以便将请求牵引至该消息)
*/
String selector ="JMSCorrelationID =\'"+messageId +"\'";
QueueReceiver qReceiver =qSession.createReceiver(replyQueue,selector);
writeToLog("QueueReceiver build with message selector[selector="+selector +"]");
long timeout =PdkProperties.singleton().getReplyTimeout();
writeToLog("Waiting for reply[timeout="+timeout +"]");
TextMessage replyMessage =(TextMessage)qReceiver.receive(timeout);
if (replyMessage ==null){
writeToLog("ERROR:No reply message received!");
setMessage("No message received");
return;
}
writeToLog("Message received from reply queue![message="+replyMessage +"]");
setMessage(replyMessage.getText());
writeToLog("Closing");
qCon.close();
/*
*If we get a reply back,we're expecting an XML message,so parse it
(如果获得了回复,那么我们就期望一个XML消息,然后对其进行解析)
311
*and fill this obj with the appropriate values(给该obj填入适应的值)
*/
//DOMBuilder docBuilder =new DOMBuilder(JDOM_ADAPTER_CLASS,false);
//java.io.ByteArrayInputStream in =
//
new java.io.ByteArrayInputStream(replyMessage.getText().getBytes());
//Document document =docBuilder.build(in);
//Element root =document.getRootElement();
/*
*Parse the doc according to an agreed upon format....(根据一致格式解析文档……)
*/
}
public void reset(){
setBalances(null);
setInterestRate(null );
setStation(null );
setTransactions(null );
setUserRole(null );
}
public void setBalances(StationsBalanceList newBalances){
balances =newBalances;
}
public void setInterestRate(com.ibm.pdk.InterestRate newInterestRate){
interestRate =newInterestRate;
}
private void setMessage(java.lang.String newMessage){
message =newMessage;
}
public void setStation(java.lang.String newStation){
station =newStation;
}
private void setTransactions(com.ibm.pdk.TransactionMap newTransactions){
transactions =newTransactions;
}
public void setUserRole(java.lang.String newUserRole){
userRole =newUserRole;
}
private void writeToLog(String message){
com.ibm.pdk.Logger.instance().write("[ViewStatementCommandImpl]-"+message);
}
}
312
LocalCommandTarget
实例:A-6 LocalCommandTarget
package com.ibm.pdk.command.util;
import java.io.*;
import java.net.*;
import java.rmi.*;
import com.ibm.websphere.command.*;
import com.ibm.pdk.Logger;
/**
*This is the CommandTarget for TargetableCommands that are executed
*locally.
(这是用于可执行的CommandTarget for TargetableCommands)
*/
public class LocalCommandTarget implements CommandTarget,Serializable {
/**
*This implements method in the CommandTarget interface.
(在CommandTarget 接口中实施方法)
*
*@param command The TargetableCommand to be executed.
*@return The TargetableCommand that has been executed.
*An example of this is the com.ibm.util.command.ejb.EJBCommandTarget.
*@exception CommandException The superclass for all command exceptions.
*/
public TargetableCommand executeCommand(TargetableCommand command)throws CommandException
{
try {
writeToLog("Executing command");
command.performExecute();
writeToLog("Command executed");
return command;
} catch (CommandException ce){
ce.printStackTrace();
throw ce;
} catch(Exception e){
e.printStackTrace();
throw new CommandException(e);
}
}
/**
*Writes to the PDK Logger singleton, which decides where and whether
*the Strings should be written.See <code>Logger</code>
(写入PDK Logger单元素,其可决定应写入哪里的字符以及是否应写入字符串。参见<code>Logger</
code>)
*
*@param message java.lang.String
*/
private void writeToLog(String message){
Logger.instance().write("[LocalCommandTarget]-"+message);
}
313
}
StationRequestListenerBean.java
实例:A-7 StationRequestListenerBean
package com.ibm.pdk.ejb.mdb;
import java.rmi.RemoteException;
import java.security.Identity;
import java.util.Properties;
import javax.ejb.*;
import java.rmi.*;
import javax.rmi.*;
import com.ibm.pdk.*;
import javax.jms.*;
import javax.naming.*;
/**
*This is a Session Bean Class(此处是一个会话Bean类)
*/
public class StationRequestListenerBean implements SessionBean {
private javax.ejb.SessionContext mySessionCtx =null;
private final static long serialVersionUID =3206093459760846163L;
/**
*ejbActivate method comment(ejbActivate 方法评论)
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbActivate()throws java.rmi.RemoteException {}
/**
*ejbCreate method comment(ejbCreate方法评论)
*@exception javax.ejb.CreateException The exception description.
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbCreate()throws javax.ejb.CreateException,java.rmi.RemoteException {}
/**
*ejbPassivate method comment(*ejbPassivate 方法评论)
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbPassivate()throws java.rmi.RemoteException {}
/**
*ejbRemove method comment(ejbRemove 方法评论)
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbRemove()throws java.rmi.RemoteException {}
/**
*@return com.ibm.pdk.ejb.mdb.StationRequestHandler
*/
public StationRequestHandler getRequestHandler(){
314
writeToLog("Getting request handler.");
StationRequestHandler requestHandler =null;
try {
InitialContext context =new InitialContext();
String jndiName =PdkProperties.singleton().getStationRequestHandlerJNDIName();
writeToLog("Looking up home interface.[jndiName="+jndiName +"]");
StationRequestHandlerHome guildHome =
(StationRequestHandlerHome)PortableRemoteObject.narrow(
context.lookup(jndiName),
StationRequestListenerHome.class);
writeToLog("Creating session bean.");
requestHandler =guildHome.create();
writeToLog("Bean created.Returning.");
} catch (NamingException e){
writeToLog("Naming exception:"+Logger.getStackTrace(e));
} catch (CreateException e){
writeToLog("Create exception:"+Logger.getStackTrace(e));
} catch (RemoteException e){
writeToLog("Remote exception:"+Logger.getStackTrace(e));
}
return requestHandler;
}
/**
*getSessionContext method comment(getSessionContext 方法评论)
*@return javax.ejb.SessionContext
*/
public javax.ejb.SessionContext getSessionContext(){
return mySessionCtx;
}
/**
*Method that is called by the JMS listener whenever a message is received on the queue.
(由JMS侦听器调用的方法在任何时候均能在队列上接收消息。)
*
*This particular implementation should be receiving XML formatted messages which will
*routed to a bean which knows how to handle the message.
(这一特殊实施应能接收要传输到bean(知道处理消息的方法)的XML格式的消息。)
*
*@param message javax.jms.Message
*@exception java.rmi.RemoteException The exception description.
*/
public void onMessage(Message message)throws java.rmi.RemoteException {
writeToLog("Forwarding message to request handler.");
getRequestHandler().execute(message);
}
public void setSessionContext(javax.ejb.SessionContext ctx)throws java.rmi.RemoteException {
mySessionCtx =ctx;
}
private void writeToLog(String message){
315
Logger.instance().write("[StationRequestListenerBean]-"+message);
}
}
GuildRequestListenerBean.java
实例:A-8 GuildRequestListenerBean.java
package com.ibm.pdk.ejb.mdb;
import java.rmi.RemoteException;
import java.security.Identity;
import java.util.Properties;
import javax.ejb.*;
import java.rmi.*;
import javax.rmi.*;
import com.ibm.pdk.*;
import javax.jms.*;
import javax.naming.*;
/**
*This is a Session Bean Class(此处是一个会话Bean类)
*/
public class GuildRequestListenerBean implements SessionBean {
private javax.ejb.SessionContext mySessionCtx =null;
private final static long serialVersionUID =3206093459760846163L;
/**
*ejbActivate method comment(ejbRemove 方法评论)
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbActivate()throws java.rmi.RemoteException {}
/**
*ejbCreate method comment(ejbCreate方法评论)
*@exception javax.ejb.CreateException The exception description. (异常描述。)
*@exception java.rmi.RemoteException The exception description. (异常描述。)
*/
public void ejbCreate()throws javax.ejb.CreateException,java.rmi.RemoteException {}
/**
*ejbPassivate method comment(ejbPassivate方法评论)
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbPassivate()throws java.rmi.RemoteException {}
/**
*ejbRemove method comment(ejbRemove 方法评论)
*@exception java.rmi.RemoteException The exception description.
*/
public void ejbRemove()throws java.rmi.RemoteException {}
/**
316
*@return com.ibm.pdk.ejb.mdb.GuildRequestHandler
*/
public GuildRequestHandler getRequestHandler(){
writeToLog("Getting request handler.");
GuildRequestHandler requestHandler =null;
try {
InitialContext context =new InitialContext();
String jndiName =PdkProperties.singleton().getGuildRequestHandlerJNDIName();
writeToLog("Looking up home interface.[jndiName="+jndiName +"]");
GuildRequestHandlerHome guildHome =
(GuildRequestHandlerHome)PortableRemoteObject.narrow(
context.lookup(jndiName),
GuildRequestListenerHome.class);
writeToLog("Creating session bean.");
requestHandler =guildHome.create();
writeToLog("Bean created.Returning.");
} catch (NamingException e){
writeToLog("Naming exception:"+Logger.getStackTrace(e));
} catch (CreateException e){
writeToLog("Create exception:"+Logger.getStackTrace(e));
} catch (RemoteException e){
writeToLog("Remote exception:"+Logger.getStackTrace(e));
}
return requestHandler;
}
/**
*getSessionContext method comment (getSessionContext方法评论)
*@return javax.ejb.SessionContext
*/
public javax.ejb.SessionContext getSessionContext(){
return mySessionCtx;
}
/**
*Method that is called by the JMS listener whenever a message is received on the queue.
*(由JMS侦听器调用的方法在任何时候均能在队列上接收消息。)
*This particular implementation should be receiving XML formatted messages which will
*routed to a bean which knows how to handle the message.
*(这一特殊的实施应能接收要传输到bean(知道处理消息的方法)的XML格式的消息。)
*@param message javax.jms.Message
*@exception java.rmi.RemoteException The exception description.
*/
public void onMessage(Message message)throws java.rmi.RemoteException {
writeToLog("Forwarding message to request handler.");(“将消息转发给请求处理者。”)
getRequestHandler().execute(message);
}
317
/**
*setSessionContext method comment(setSessionContext方法评论)
*@param ctx javax.ejb.SessionContext
*@exception java.rmi.RemoteException The exception description.
*/
public void setSessionContext(javax.ejb.SessionContext ctx)throws java.rmi.RemoteException
{
mySessionCtx =ctx;
}
/**
*Writes messages to the application log.(向应用程序日志中写入消息)
*@param message java.lang.String
*/
private void writeToLog(String message){
com.ibm.pdk.Logger.instance().write("[GuildRequestListenerBean]-"+message);
}
}
318
B
从VisualAge for Java运行 JMSAdmin
在开发和单位测试过程中,开发人员需要测试其消息发送应用程序的环境。在 VisualAge
for Java 的 WebSphere 测试环境内,有个“永久名称服务器”,它属于 IDE 的命名服务。
将此服务与 JMS 管理工具结合使用,开发人员可以创建 JMS 管理对象,并在其应用程
序中引用这些管理对象。
从 VisualAge for Java 运行 JMS 管理工具(在以下指令中假定读者了解 VisualAge for Java
的基本知识):
确保已将以下存档文件导入 VisualAge:
1.
-
com.ibm.mqjms.jar
-
com.ibm.mq.jar
2.
确保已将 WebSphere 测试环境功能添加到工作区(Workspace)。
3.
从 您 已 导 入
VisualAge
的
com.ibm.mqjms.jar
文 件 中 找 到
com.ibm.mq.jms.admin.JMSAdmin 类。
4.
类的属性编辑命令行参数以及该类的 CLASSPATH。
a.
在“类路径”选项卡上,通过单击“立即计算……”按钮计算项目类路径。
应添加下列项目:
319
IBM Enterprise Extension Libraries
MQSeries for Java——这是包含从 com.ibm.mq.jar 导入的类的项目
IBM XML Parser for Java
IBM WebSphere 测试环境
安全套接字层
b.
在“程序”选项卡上,添加命令行参数,以便为管理工具指定配置文件。例
如,假定配置文件位于下面的目录(默认目录),添加以下命令(注释:如
果路径中还有空间,请使用短目录名称或把路径放在引号里面):
-cfg c:\progra~1\ibm\mqseries\java\bin\JMSAdmin.config
5.
编辑配置文件,并确保值与第 217 页的实例 12-1 中显示的值相匹配。
6.
通过选择“工作区”—>“工具”—>“测试环境”,启动“WebSphere 测试环境......”。
7.
在启动测试环境后,单击“服务器”—>“永久名称服务器”。
8.
当永久名称服务器的选项出现在窗口的右边时,单击“启动名称服务器......”。
9.
当您在“控制台”中看到以下行时,就说明已启动名称服务器,而且已准备好运
行管理工具:
4417 EJServer
E Server open for business.
(提示:名称服务器的默认端口为 900。此端口与 WebSphere 要使用的端口相同,
因此应确保 WebSphere 不在运行,或者更改 VisualAge 和配置文件中的 bootstrap 端
口号。)
10.
运行 JMSAdmin 类。您应该可以看到与图 B-1 相似的输出。如果看不到 InitCtx>
提示符,请检查名称服务器是否正常启动。另外检查配置文件中的值是否正确。
320
图 B-1 在 VisualAge for Java 里的控制台窗口
在“Standard In”窗格中输入针对管理工具的命令。例如,输入以下命令查看当前上下
文中的捆扎:
display ctx
321
图 B-2 初始上下文内容
11.
这样就设置好了管理工具,并且可从 VisualAge 运行了。要确认一切工作正常,请
完成下一节,以便运行包含 JNDI 支持的 PTPSample01。
运行具有 JNDI 支持的 PTPSample01 实例
本节描述如何从 JNDI 支持的 VisualAge 内运行 PTPSample01。
运行具有 JNDI 支持的实例:
1.
确保已将实例导入 VisualAge。如果没有,您可以从以下目录导入实例:
<MQSeries Java Install>\samples\jms
2.
322
确保永久名称服务器正在运行。
3.
运行 JMS 管理工具,并键入以下命令,把 QueueConnectionFactory(由字符串
“ivtQCF“ 捆扎)和 Queue(由名称 “ivtQ“ 捆扎)添加到 JNDI 命名空间之中:
define qcf(ivtQCF)
define q(ivtQ)queue(SYSTEM.DEFAULT.LOCAL.QUEUE)
4.
最后,通过编辑类的属性,编辑实例的“类路径”和命令行参数。
a.
在“程序”选项卡上,用以下行替换任何现有参数:
-url iiop://localhost -icf com.ibm.ejs.ns.jndi.CNInitialContextFactory
b.
5.
在“类路径”选项卡上,单击“立即计算......”按钮,计算程序类路径。
最后,运行该实例。如果程序运行成功,您就会看到与以下实例输出一样的输出。
实例 14-32 从 JNDI 支持的 PTPSample01 输出
5648-C60 (c)Copyright IBM 公司。1999年,保留所有版权。
支持Java(tm)Message Service的MQSeries Classes-端对端实例1
Retrieving a QueueConnectionFactory from JNDI(从JNDI检索QueueConnectionFactory)
Creating a Connection(创建连接)
Starting the Connection(启动连接)
Creating a Session(创建会话)
Unable to load message catalog -mqji(不能加载消息目录-mqji)
Retrieving a Queue from JNDI
Creating a QueueSender
Creating a QueueReceiver
Creating a TextMessage
Adding Text
Sending the message to queue:///SYSTEM.DEFAULT.LOCAL.QUEUE(将消息发送到队列:
///SYSTEM.DEFAULT.LOCAL.QUEUE)
Reading the message back again(再次读取消息)
Got message:(获取消息:)
JMS Message class:jms_text(JMS消息类:jms_text)
JMSType:null
JMSDeliveryMode:2
JMSExpiration:0
JMSPriority:4
JMSMessageID:ID:414d5120514d5f6270726f636f70696f506ce53b12700000
JMSTimestamp:1004902675530
JMSCorrelationID:null
JMSDestination:queue:///SYSTEM.DEFAULT.LOCAL.QUEUE
JMSReplyTo:null
JMSRedelivered:false
JMS_IBM_MsgType:8
JMSXAppID:for Java\ide\program\IDE.EXE
323
JMSXUserID:Administrato
JMSXDeliveryCount:1
JMS_IBM_PutApplType:11
JMS_IBM_Format:MQSTR
A simple text message from PTPSample01(PTPSample01的简单文本消息)
Reply string equals original string(回复同等于原始字符串的字符串)
Closing QueueReceiver
Closing QueueSender
Closing Session(关闭会话)
Closing Connection(关闭连接)
finished(完成)
提示:代码行 Unable to load message catalog –mqji 只是一个警告,指明包含各种消息的
属性文件不在 LASSPATH 上。这并不是严重错误,但是,如果您不想修复此错误,就
从<MQSeries Java Install >/lib 目录中添加 .properties 文件。
324
相关出版物
该部分所列出的出版物都是适合对这本红皮书所涉及到的主题进一步详细的探讨的。
IBM红皮书
想了解如何订购这些出版物, 请参看 327 页“如何得到 IBM 红皮书”。
《XML 文件:企业到企业和企业到客户应用程序使用 XML》,编号:SG24-6104
《自助服务模式使用 WebSphere Application Server 版本 4.0》,编号:SG24-6175
《Tivoli SecureWay 安全指南:集中管理电子商务安全》,编号:SG24-6008
《使用 Tivoli Policy Director 的应用程序和消息发送安全》,编号:SG24-6024
《MQSeries 版本 5.1 管理和编程实例》,编号:SG24-5849
《采用 WebSphere 高级版的 CCF 连接器和数据库连接》,编号:SG24-5514
《WebSphere 版本 4 应用程序开发手册》,编号:SG24-6134
《使用 WebSphere Everyplace Access Suite 的移动应用程序:设计和开发》,编号:
SG24-6259
其他资源
以下也是 IBM 的一些相关出版物:
《MQSeries Integrator 编程指南版本 2.0.2》,编号: SC34-5603-02
《采用控制中心的 MQSeries Integrator 版本 2.0.2》,编号:SC34-5602-03
《MQSeries Integrator 入门和设计版本 2.0.2》,编号:GC34-5599-02
《MQSeries 系统管理》,编号:SC33-1873
《MQSeries 可编程的系统管理》,编号:SC33-1482
《MQSeries 管理接口编程指南》,编号:SC33- 5390
《MQSeries 命令参考》,编号:SC33-1369
325
《MQSeries:队列管理器群集》,编号:SC34-5349
《MQSeries 使用 Java》,编号:SC34-5456-06
《电子商务模式:可复用策略》,由 Jonathan Adams、Srinivas、 Koushik、Guru
Vasudeva 和 George Galambos 编著,IBM Press 出版,ISBN:1-931182-02-7
《Nutshell 中的 Java Enterprise》,由 David Flanagan、Jim Farley、William Crawford
和 Kris Magnusson 编著
《JavaScrip:权威指南》第三版,由 David Flanagan、O'Reilly 和 Associates 公司合
编,1998 年
《XML 和 Java:开发 Web 应用程序》,由 Maruyama、Hiroshi、Kent Tamura、
Naohiko Uramoto 和 Addison-Wesley 编著,1999 年
可供参考的网站
如欲了解详情,可到以下相关网站浏览:
IBM MQSeries Family SupportPacs
http://www.ibm.com/software/ts/mqseries/txppacs/
IBM MQSeries 主页
http://www.ibm.com/software/ts/MQSeries
JMS 规范
http://java.sun.com/products/jms/index.html
Java 命名和目录(JNDI) API
http://java.sun.com/products/jndi/index.html
开放源 XML 框架
http://xml.apache.org/
管理 MQSeries
http://www.software.ibm.com/ts/mqseries/library/independent/nasg/vendor.htm
l
MessageQ.Com
http://www.messageq.com
ECMAScript 语言规范
http://www.ecma.ch/stand/ECMA-262.htm
Java APIs 和技术
http://java.sun.com/products
326
IBM 电子商务软件策略论文包括:
-
《电子商务 IBM 应用程序框架:精明技术选择》
-
《电子商务 IBM 应用程序框架:结构概览》
http://www.ibm.com/software/ebusiness/docs/index.html
Validator 工具
http://validator.w3.org
http://jigsaw.w3.org/css-validator/
Bluetooth 网站
http://www.bluetooth.com
泛计算中的 Bluetooth 应用程序白皮书
http://www.ibm.com/pvc/tech/bluetoothpvc.shtml
如何得到IBM红皮书
您可从以下红皮书网站找到更多的红皮书或红皮稿以便查看和下载,也可定购硬拷贝
版 :
ibm.com /redbooks
该网站还提供了一些附属资料(如编码样本或磁盘/光盘的图象)的下载。
红皮稿是处理中的红皮书; 但并非所有红皮书都以红皮稿或只有几个章节的形式出版。
这可比正式出版进程更快地获得所需信息。
IBM红皮书集
红皮书也提供光盘版。如想了解光盘版的供应,更新以及格式等具体情况,您可以点击
我们红皮书网站上的光盘版按钮。
327
328
特别声明
在本出版物中所提到的 IBM 产品、程序或服务并不意味着 IBM 将为所有 IBM 运作的国
家提供。任何对 IBM 产品、程序或服务的引用并不说明或暗示只能使用 IBM 的产品、
程序或服务。凡是同等功能的产品、程序或服务,只要不侵犯 IBM 的知识产权,都可
以用来代替 IBM 产品、程序或服务。
本文档中的信息和特定设备的使用介绍结合在一起,但仅限于使用那些特定硬件和软件
产品和标准的应用程序。
IBM 可能已经申请或正在申请与本文档有关的各项专利权。提供本文档并不表示允许您
使用这些专利。您可以用书面方式将特许查询寄往: IBM Director of Licensing, IBM
Corporation, North Castle Drive, Armonk, NY 10504-1785. USA 。
为了以下目的:
(i) 允许在独立创建的程序和其它的程序(包括本程序)之间进行信息
交换和 (ii) 允许对已经交换的信息进行相互使用,而希望获取本程序有关信息的合
法用户请与下列地址联系: IBM Corporation, Dept. 600A, Mail Drop 1329, Somers,
NY 10589 USA 。
这些信息可以通过适当的条款和条件来得到,包括在一些案例中或支付一定的费用。
本文档包含的信息并没有提交给任何正式的 IBM 测试并发布。本信息的使用或任何这
些技术的执行是客户的责任,并依靠客户的能力来评估它们,将其整合到客户的操作环
境中。如果其中的条款由 IBM 在特定情形下被正确地校订,并不能保证在别处也可以
得到相同或相似的结果。如果客户企图修改在他们自己环境下的这些技术,将自己承担
风险。
本资料中对非 IBM Web 站点的任何引用只是为方便您而提供的,IBM 不以任何方式对
这些 Web 站点作保证。
329
以下术语是其它公司的商标:
C-bus 是 Corollary 公司在美国和/或其它国家的商标。
Java 及所有基于 Java 的商标及徽标均为 Sun 微系统公司在美国和/或其它国家的商标或
注册商标
Microsoft、Windows、Windows NT、Windows 商标均为微软公司在美国和/或其它国家
的商标。
PC Direct 是 Ziff 通信公司在美国和/或其它国家的商标,并且许可 IBM 公司使用。
ActionMedia、LANDesk、MMX、Pentium 和 ProShare 是 Intel 在美国和/或其它国家的商
标。
UNIX 是在美国及其它通过 Open Group 唯一特许的国家的注册商标。
SET、SET Secure Electronic Transaction 和 SET 标志是 SET Secure 电子交易公司的商标。
其它的公司、产品和服务名称可能是其自己的商标或服务标志。
330
Fly UP