消息目标
使用 JMS 的 J2EE 应用程序通常分为两个阵营:
使用 JMS 与后端系统通信的应用程序。
使用 JMS 提供与应用程序的不同部分异步通信的应用程序。
在这一节中,我将描述在 J2EE 规范中的一个改变,它使第二种类型的应用程序更容易部署。作为例子,我将展示两个 EJB 组件,它们通过彼此发送消息进行通信。在 J2EE 1.3 中,每个 bean 都要在其部署描述符中定义一个 resource-env-ref ,它在以后要在其本地命名空间中查询它。在清单 2 所示的例子中, SenderEJB 将从 java:/comp/env/jms/target 中查询其目标,而 ReceiverEJB 将从 java:/comp/env/jms/source 中查询其目标。因为引用在不同的本地命名空间中,所以没有一种机制让应用程序汇编器(application assembler )向部署人员表明这些 resource-env-ref s 实际上应当绑定到同一个目标。
清单 2. 显示 resource-env-ref 的部署描述符代码片段
2 <enterprise-beans>
3 <session>
4 <ejb-name>SenderEJB</display-name>
5 ...
6 <resource-env-ref>
7 <resource-env-ref-name>jms/target</resource-env-ref-name>
8 <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
9 </resource-env-ref>
10 ...
11 </session>
12 <session>
13 <ejb-name>ReceiverEJB</display-name>
14 ...
15 <resource-env-ref>
16 <resource-env-ref-name>jms/source</resource-env-ref-name>
17 <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
18 </resource-env-ref>
19 ...
20 </session>
21 </enterprise-beans>
22 </ejb-jar>
23
在 J2EE 1.4 中,仍然可以使用 resource-env-ref 定义目标,不过,还有一个与 resource-env-ref 非常类似的新元素 message-destination-ref ,它还有两个子元素。其中第一个是 message-destination-usage ,正如它的名字所表明的,它用于向部署人员表明应用程序准备如何使用目标。它可以取以下的值中的一个:
Produces
Consumes
ProducesConsumes
因而部署人员通常会将 Produce 的引用和另一个 Consume 的引用绑定到同一个目标上。
增加的第二个元素是 message-destination-link 。它可用于将两个或者更多 message-destination-ref 联系到一起。在 link 中包含的名字必须与在另一个新元素—— message-destination ——中给定的名字相匹配。部署应用程序后,部署人员将一个 message-destination 绑定到一个 JMS 目标,所有 message-destination-ref s 都使用这同一个绑定。
因此在 J2EE 1.4 中,清单 2 中的例子现在可以改写为如清单 3 所示的形式。
清单 3. 带有 message-destination-ref 和 message-destination 元素的部署描述符代码片段
2 <enterprise-beans>
3 <session>
4 <ejb-name>SenderEJB</display-name>
5 ...
6 <message-destination-ref>
7 <message-destination-ref-name>jms/target</message-destination-ref-name>
8 <message-destination-type>javax.jms.Queue</message-destination-type>
9 <message-destination-usage>Produces</message-destination-usage>
10 <message-destination-link>destination</message-destination-link>
11 </message-destination-ref>
12 ...
13 </session>
14 <session>
15 <ejb-name>ReceiverEJB</display-name>
16 ...
17 <message-destination-ref>
18 <message-destination-ref-name>jms/source</message-destination-ref-name>
19 <message-destination-type>javax.jms.Queue</message-destination-type>
20 <message-destination-usage>Consumes</message-destination-usage>
21 <message-destination-link>destination</message-destination-link>
22 </message-destination-ref>
23 ...
24 </session>
25 </enterprise-beans>
26 <assembly-descriptor>
27 ...
28 <message-destination>
29 <message-destination-name>destination</message-destination-name>
30 </message-destination>
31 ...
32 </assembly-descriptor>
33 </ejb-jar>
34
为了强调这两种新元素所造成的不同,请看以下两图,在每个图中,EAR 文件用蓝颜色遮蔽,表示应用程序汇编器的影响范围。应用程序部署人员添加红色箭头以将在应用程序中定义的资源绑定到实际的 JMS 队列。在图 1 中,可以看到应用程序汇编器有两个 resource-env-ref ,每个 EJB 组件有一个,并必须保证部署人员知道它们应当绑定到同一个 JMS 目标。
图 1. 使用 resource-env-ref 的示例应用程序

与此相反,在图 2 中,应用程序汇编器可以通过将两个 message-destination-ref 链接到同一个 message-destination ,而清楚地表明他或者她的意图。之后部署人员只需要添加一个绑定。
图 2. 使用 message-destination 的示例应用程序

与 ejb-link 使用的语法相同,还可以链接到在同一应用程序中不同 JAR 文件中定义的 message-destination 。例如,
消息驱动 bean
本文介绍的 J2EE 消息系统的最后一个改变是消息驱动 bean (MDB) 的改变。在 EJB 2.0 规范中,MDB 必须实现接口 javax.jms.MessageListener 。在 EJB 2.1 规范中,已经不再严格要求了,因此 MDB 可以实现 任何接口,只要有人准备好向它传送消息!对于 JMS 提供者,一般仍然实现 javax.jms.MessageListener ,的确不需要改变 MDB 代码就可以接纳新的规范。不过为了支持其他接口,部署描述符有了很大改变。
清单 4 展示了 EJB 2.0 应用程序的 MDB 部署描述符的一个典型例子。清单 5 展示了同一 MDB 用在 EJB 2.1 应用程序中时,部署描述符会是什么样子。
清单 4. EJB 2.0 部署描述符
2 <enterprise-beans>
3 <message-driven>
4 <ejb-name>DurablePubSubMDB</ejb-name>
5 <ejb-class>example.ExampleMDB</ejb-class>
6 <transaction-type>Bean</transaction-type>
7 <acknowledge-mode>Auto-acknowledge</acknowledge-mode>
8 <message-driven-destination>
9 <destination-type>javax.jms.Topic</destination-type>
10 <subscription-durability>Durable</subscription-durability>
11 </message-driven-destination>
12 <message-selector>
13 JMSType = 'person' AND gender = 'male'
14 </message-selector>
15 </message-driven>
16 </enterprise-beans>
17 </ejb-jar>
18
清单 5. EJB 2.1 部署描述符
2 <enterprise-beans>
3 <message-driven>
4 <ejb-name>DurablePubSubMDB</ejb-name>
5 <ejb-class>example.ExampleMDB</ejb-class>
6 <messaging-type>javax.jms.MessageListener</messaging-type>
7 <transaction-type>Bean</transaction-type>
8 <activation-config>
9 <activation-config-property>
10 <activation-config-property-name>
11 acknowledgeMode
12 </activation-config-property-name>
13 <activation-config-property-value>
14 Auto-acknowledge
15 </activation-config-property-value>
16 </activation-config-property>
17 <activation-config-property>
18 <activation-config-property-name>
19 destinationType
20 </activation-config-property-name>
21 <activation-config-property-value>
22 javax.jms.Topic
23 </activation-config-property-value>
24 </activation-config-property>
25 <activation-config-property>
26 <activation-config-property-name>
27 subscriptionDurability
28 </activation-config-property-name>
29 <activation-config-property-value>
30 Durable
31 </activation-config-property-value>
32 </activation-config-property>
33 <activation-config-property>
34 <activation-config-property-name>
35 messageSelector
36 </activation-config-property-name>
37 <activation-config-property-value>
38 JMSType = 'person' AND gender = 'male'
39 </activation-config-property-value>
40 </activation-config-property>
41 </activation-config>
42 </message-driven>
43 </enterprise-beans>
44 </ejb-jar>
45
首先要注意的是新的 messaging-type 元素,它给出了 MDB 实现的接口类的名字。其次是删除了 acknowledge-mode 、 destination-type 、 subscription-durability 和 message-selector 。取而代之的是一组通用的名-值对。对于 JMS 提供者,JCA 规范建议支持表 1 中给出的名字和值。
表 1. 建议的 JMS activation-config-property 值 activation-config-property-name activation-config-property-value destinationTypejavax.jms.Queue or javax.jms.TopicacknowledgeModeAuto-acknowledge (default) or Dups-ok-acknowledgesubscriptionDurabilityNonDurable (default) or DurablemessageSelectorString selector
不幸的是,这些属性只是建议性的,不是每一个 JMS 提供者都一定支持它们。如何知道所使用的提供者支持哪些属性呢?在 JCA 规范的“Message Inflow”一章中,规定了 JMS 提供者与向 MDB 发送消息的应用服务器之间的协议。作为这个协议的一部分,JMS 提供者实现一个称为 ActivationSpec 的 JavaBean 组件,它的属性包含那些提供者发送消息所需要的信息。管理员可以为这些属性定义默认值,但是部署包含 MDB 的应用程序时,在 MDB 的部署描述符中定义的任何 activation-config-property 元素都会覆盖它们。因而遵守该建议的 JMS 提供者在 ActivationSpec 上有名为 destinationType 、 acknowledgeMode 、 subscriptionDurability 和 messageSelector 的属性。
注意在这个新模型中,可以不让属性出现在部署描述符中,而管理式地定义它。另一方面,可能有另外一些特定于提供者的属性,以前必须管理式地定义,现在则可加入到 MDB 的部署描述符中(尽管这显然降低了应用程序的可移植性)。
建议 JMS ActivationSpec 具备的另一个属性是 destination 。不幸的是,JCA 规范没有定义这个属性的类型或者它的意义。例如,它可以是要求部署人员为目标指定某个特定于提供者的名字的字符串。一种特殊的情况是属性的类型是 javax.jms.Destination 。在这种情况下,应用程序汇编器可以在 MDB 的部署描述符中指定一个 message-destination-link ,以表明 MDB 应当从中收取消息的 message-destination 。这个链接不是 message-destination-ref 的一部分,它与 message-destination-type 都定义为 message-driven 元素的直接子元素,如清单 6 的示例部署部署描述符所示。在运行时,应用服务器可以将链接解析为一个 JMS 目标对象、并将它设置到 ActivationSpec 的 destination 属性中。
清单 6. 显示一个 MDB message-destination-link 的 EJB 2.1 部署描述符
2 <enterprise-beans>
3 <message-driven>
4 <ejb-name>DurablePubSubMDB</ejb-name>
5 <ejb-class>example.ExampleMDB</ejb-class>
6 <messaging-type>javax.jms.MessageListener</messaging-type>
7 ...
8 <message-destination-type>
9 javax.jms.Queue
10 </message-destination-type>
11 <message-destination-link>
12 destination
13 </message-destination-link>
14 ...
15 </message-driven>
16 ...
17 </enterprise-beans>
18 ...
19 </ejb-jar>
20
结束语
在 J2EE 1.4 规范中,JMS 应用程序有许多改变和新功能。有一些改变,比如在 Web 容器上使用 setMessageListener() 的更严格限制,限制每个 Connection 有一个 Session ,以及有很大变化的 MDB 部署描述符,它们将要求修改现有的应用程序。其他改变,如统一的 JMS 1.1 API,则会简化新应用程序的编程并扩展现有的功能。不论是否正在移植 J2EE 1.3 应用程序或者有机会编写新的应用程序,我希望随着越来越多的 J2EE 1.4 应用服务器出现,您会发现本文会很有用。