扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
JMS定义了Java中访问消息中间件的接口,是企业资源异步访问的主要形式。JMS可以和EJB技术集成使用,也就是常说的消息驱动BEAN。
因为访问JMS和访问Entity EJB和SessionEJB的调用方法有很大的差异,所以Beehive中提供了专门的JMS控件来完成JMS资源的访问。
创建JMS控件的典型步骤
Beehive中提供的JMS控件无法直接用于访问JMS资源,使用者必须继承JMS控件来创建自己的访问控件,设置相关的环境后才能完成JMS资源的访问。一个继承控件中只能对应的访问唯一一个消息队列/主题,如果需要向多个消息队列/主题发送消息,必须针对每个消息队列/主题提供相应的控件。
你可以使用下面的步骤来创建自己的控件实现对JMS资源的访问:
创建新的Java接口,继承JMSControl接口
用@ControlExtension关键字注释新创建的Java接口,通知解析器这个接口继承了另外的某个控件。
使用@JMSControl.Destination注释新创建的Java接口,说明控件如何搜索消息队列/主题,同时使用sendType声明发送目标是消息队列还是消息主题。
创建新的业务方法,使用JMSControl.Message注释来声明被发送消息的类型,或者使用不同的参数来区别不同的消息类型。
使用控件访问消息队列的例子
在本节中,我们将通过简单的例子演示如何使用JMS控件来访问监听消息队列的消息驱动Bean。我们假设存在一个消息驱动Bean,他接受用户传递的文本消息(TextMessage),随后将该文本消息打印在应用服务器的控制台上。该消息驱动Bean监听消息队列(jndiname=”queue/helloworld”)。
Beehive并不仅仅限于WebLogic平台使用,我们这里以JBoss为例。我们使用Eclipse+WTP(Web Tools Platform)工具开发符合要求的消息驱动Bean以及JMS队列,发布在JBoss上。
本文中所有例子的源代码可以在通过资源下载区中的连接完成下载。
开发消息驱动Bean
现在我们来开发提供上述功能的消息驱动Bean,使用WTP的向导完成消息驱动Bean的创建工作,剩下的工作就是根据业务的需求完成onMessage方法的编写了。
根据演示实例的要求,我们修改onMessage方法,让它处理TextMessage类型的消息,并且将消息的内容打印在控制台上,清单1中列出了消息驱动Bean类的源代码。如何开发、编译和部署请参考JBoss的帮助文档,这里不提供详细的说明。
接下来的JMS、EJB开发、部署和控件访问部分内容针对JBoss应用服务器。实际情况下,你可能根据不同情况选择其他的J2EE容器比如Websphere 、WebLogic、Geronimo、JOnAS等作为EJB容器,请根据下面的提示信息进行适当的调整。
清单1 ejbsrcorgvivianjeehivecontrolsexamplesejbMessageBeanOnQueue.java
1. package org.vivianj.beehive.controls.examples.ejb;
2.
3. import javax.ejb.*;
4. import javax.jms.*;
5.
6. public class MessageBeanOnQueue
7. implements MessageDrivenBean, MessageListener
8. {
9. public void onMessage(Message msg) {
10. TextMessage tm = null;
11. if (msg instanceof TextMessage){
12. tm = (TextMessage) msg;
13. try{
14. System.out.println(tm.getText());
15. }catch(Exception e){
16. e.printStackTrace();
17. }
18. }
19. }
20. }
在部署消息驱动Bean之前,我们还应该在JBoss服务器上配置一个JNDI名称为“queue/helloworld”的消息队列。请大家参考JBoss应用服务器的帮助文档完成这部分工作。
JMS控件开发
现在我们来使用JMS控件编写代码访问消息驱动Bean。由于消息驱动Bean没有本地或者远程访问方法,只能通过向JMS目标发送消息来完成消息驱动Bean资源的调用。
清单2中的代码是访问一个JMS目标对列的简单例子,它提供sendTextMessage方法,接收String类型的参数msn,当sendTextMessage方法被调用时,控件负责连接到JMS对列(“queue/helloworld”),将接收到的参数msg发送到目标服务器上。
清单2 srcorgvivianjeehivecontrolsexamplescontrols
HelloWorldQueueJMSControl.java
1. package org.vivianj.beehive.controls.examples.controls;
2.
3. import org.apache.beehive.controls.api.bean.ControlExtension;
4. import org.apache.beehive.controls.system.jms.JMSControl;
5.
6. /**
7. * HelloWorldQueueJMSControl 是访问JMS资源的控件
8. * 可向JMS对列发送字符串类型的消息
9. */
10.
11. @ControlExtension
12. @JMSControl.Destination(sendJndiName = “queue/helloworld”,
13. jndiConnectionFactory =
14. “org.jnp.interfaces.NamingContextFactory”,
15. jndiProviderURL=“jnp://localhost:1099”,
16. sendType=DestinationType.Queue)
17. public interface HelloWorldQueueJMSControl
18. extends JMSControl {
19. @JMSControl.Message(MessageType.Text)
20. public void sendTextMessage(String msg);
21. }
JMS控件调用
现在我们可以使用如下方法来完成上面所创建控件的调用。
使用声明式控件实例化定义成员变量_jmsControl.
@Control
HelloWorldQueueJMSControl _jmsControl;
调用控件的业务方法完成向JMS对列发送消息的功能。由于消息服务本身是没有返回内容的,所以我们只需要完成业务方法的调用即可。
下面的调用代码可以实现向服务器发送”Hello World!”字符串的功能。
_jmsControl.sendTextMessage(“Hello World!”);
测试控件
一切准备就绪后,启动JBoss服务器,参考《控件入门》中“使用JUnit测试控件”部分的内容,编写单元测试TestCase测试新创建的JMS控件。
实例分析
从上面的例子中我们可以看到,访问JMS资源的时候,开发者的工作被大大的简化了。开发者只需要开发一个继承自JMS控件的控件,使用JMS控件中规定的注释提供访问JMS资源所需要的一些环境参数,随后便可以使用声明式控件实例化方式完成控件的实例化,通过调用该控件实例的相关业务方法完成JMS资源的访问。
现在我们来分析一下上面创建的EJB控件-- HelloWorldQueueJMSControl (参见清单2)中的主要代码。
11. @ControlExtension
在控件例子的11行,我们通过@ControllerExcention关键词来说明接下来声明的这个接口是另外一个控件的扩展。
12. @JMSControl.Destination(sendJndiName = “queue/helloworld”,
13. jndiConnectionFactory =
14. “org.jnp.interfaces.NamingContextFactory”,
15. jndiProviderURL=“iiop://localhost:7001”,
16. sendType=DestinationType.Queue))
在第12~16行代码中我们使用JMSControl.Destination关键词和它的sendJndiName、jndiConnectionFactory、jndiProviderURL和sendType属性来设置我们要访问消息队列/主题在目标服务器中发布时使用的jndiName和访问目标服务器需要提供的相关环境变量。
17. public interface HelloWorldQueueJMSControl
18. extends JMSControl {
在第17~18行中,我们声明该控件继承了JMSControl接口。
19. @JMSControl.Message(MessageType.Text)
在第19行,我们使用@JMSControl.Message关键字来声明接下来的这个业务方法发送消息的类型是TextMessage。
20. public void sendTextMessage(String msg);
第20行代码我们声明了一个方法,这个方法需要向目标消息队列/主题发送参数msg提供的字符串。
完成这些工作,我们就可以调用该控件声明的方法实现向目标消息队列/主题发送JMS消息了,如何与目标应用服务器交互的工作由控件来辅助完成。
使用注释定制JMS控件
在上面的内容中,我们已经新建了一个JMS控件,它提供sendTextMessage方法,调用该方法能够向消息对列”queue/helloworld”发送消息。在JMS控件代码中,我们使用了@JMSControl.Destination我们使用了@JMSControl.Destination和@JMSControl.Message两个注释来提供访问JMS资源的参数。
JMS控件支持10个注释,他们分别是@JMSControl.CorrelationId、@JMSControl.Delivery 、@JMSControl.Destination 、@JMSControl.Expiration 、@JMSControl.Message 、@JMSControl.Priority 、@JMSControl.Properties 、@JMSControl.Property 、@JMSControl.PropertyValue 、@JMSControl.Type
下面我们详细的介绍其中最常用的@JMSControl.Destination和@JMSControl.Message两个注释的属性和用法,了解如何用这注释定制JMS控件的更多细节。
Destination注释
Destination注释是类级别的注释,用于定制JMS控件发送消息的目标消息队列/主题的jndiName和它们所在目标容器的相关参数。Destination注释提供了八个参数用于用户定制,下面将介绍常用的四个:sendJndiName、jndiConnectionFactory、 sendType、jndiProviderURL,其中sendJndiName和jndiConnectionFactory是必须的,其他的可以根据不同的情况选择性的使用。
sendJndiName
字符串类型的属性,用于设置目标消息队列或者主题的JNDI名称。
sendType
JMSControl.DestinationType类型的属性,可选值有三个:DestinationType.Auto(自动适配类型),DestinationType.Topic(主题)和DestinationType.Queue(队列),如果使用DestinationType.AUTO,控件将根据sendJndiName中指定的目标消息类型进行适配。
jndiConnectionFactory
字符串类型的属性,设置访问EJB JNDI上下文环境需要使用工厂类,和具体的目标EJB服务器相关。比如访问JBoss服务器上的JMS资源时可以设置jndiConnectionFactory为“org.jnp.interfaces.NamingContextFactory”。
jndiProviderURL
字符串类型的属性,设置目标EJB容器的相关属性,包括访问JNDI上下文环境使用的协议、目标服务器IP地址、服务端口等,比如访问JBoss容器中消息队列时,providerURL可以写成”jnp://localhost:1099”。
Message注释
Message注释是JMS控件中方法级别的注释,它的参数为JMSControl.MessageType类型,用于注释新创建JMS控件的业务方法,声明该业务方法发送消息的类型,可选参数包括MessageType.Text、MessageType. Bytes(字节类型)、MessageType.Object(对象类型), MessageType.Map (Map类型)、MessageType.JMSMessage(JMS消息类型)、MessageType.Auto(自动适配类型),默认的消息类型是MessageType.Auto。
如果使用默认的消息类型或者设置被发送消息的类型为MessageType.Auto,控件将根据被发送消息的类型来决定发送消息时使用的消息类型:
如果被发送的消息是字符串内容或者是XML对象,控件将发送TextMessage类型的消息给目标消息队列/主题;
如果被发送的消息内容是字节数组,控件将发送StreamMessage类型的消息给目标消息队列/主题;
如果被发送的消息内容是字节数组,控件将发送StreamMessage类型的消息给目标消息队列/主题;
如果被发送的消息内容是Map对象,控件将发送MapMessage类型的消息给目标消息队列/主题;
如果被发送的消息内容是JMSMessage对象,控件将发送JMSMessage类型的消息给目标消息队列/主题;
如果被发送的消息内容实现了Serializable接口,控件将发送ObjectMessage类型的消息给目标消息队列/主题;
其他的消息内容,控件将不发送消息,而是抛出一个违例。
JMS控件内置的方法
JMS控件提供了很多可以在运行时对JMS控件进行定制的Java方法,开发者使用这些内置的方法可以在使用JMS控件的过程中根据不同的情况灵活的加以应用,以便能够完成各种复杂的业务逻辑。
getSession()
获取当前访问消息队列/主题的会话对象。
getDestination()
获取当前访问消息队列/主题的目标服务的相关信息。
getConnection()
获取当前访问消息队列/主题和目标服务器之间的连接。
setHeaders(Map)
setHeader(HeaderType,Object)
这两个方法都用于设置被发送消息的头信息,只有调用了这两个方法后接着发送的消息才使用这部分头信息,其他的消息不具备这些头信息。如果发送的消息中也设置了同样的信息,使用setHeaderX方法设置的参数将覆盖消息中设置的参数。
被设置的头信息包括JMSCorrelationID、JMSExpriation 、Priority 、JMSType,用于发送消息时标识身份或者提供消息发送所需要的路由。
setProperties(Map)
setProperty(String,Object)
这两个方法用于在发送消息的同时提供更多的属性信息,可以用于在应用中作为扩展头信息而使用,可发送的类型可以是boolean, byte, short, int, long, float, double, or String等基本Java数据类型。
结束语
JMS是J2EE框架中最重要的部分,也是企业应用中提供异步消息访问的技术实现,然而JMS的客户端编写对于开发者而言不是一件轻松的事情。控件架构中的JMS控件大大的简化了JMS资源的复杂性、难度,开发者只需要通过简单的继承org.apache.beehive.controls.system.jms.JMSControl,然后通过提供相应的注释就可以完成JMS资源的访问。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者