`
yangzb
  • 浏览: 3502885 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JMS request/reply from an EJB

    博客分类:
  • Java
阅读更多

Sometimes something that appears trivial turns out to be a lot more involved. Doing JMS request/reply from an EJB is such a case. Let's take a look...

Request/reply

JMS can be used as a form of inter process communication. JMS is meant for asynchronous calls, but it can also used for synchronous calls. In that case, the first process sends a message to a queue or topic, and waits until another process has replied to the message. The reply is sent to a different queue or topic. Advantages of using JMS for interprocess communication is that the two processes are completely decoupled. The two processes only need to agree on what is in the message.

Synchronous communication in JMS is also called request/reply.

The JMS api provides two helper classes to make request/reply easier. They are the QueueRequestor and TopicRequestor classes. Here is an example of how they can be used:

QueueConnection c = (QueueConnectionFactory) (new
InitialContext().lookup("qcf"));
QueueSession s = c.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue q = (Queue) (new InitialContext().lookup("requestqueue"));
QueueRequestor r = new QueueRequestor(s, q);
Message reply = r.request(s.createTextMessage("1233442342"));
c.close();

Looks simple, right? However, when this code is used in a typical EJB, it will likely not work. Why not? Is there something wrong with the QueeuRequestor ? Well, yes, yet that is not the problem. Let's look at the implementation of the QueueRequestor :

public class QueueRequestor {
 QueueSession session; 
 Queue queue; 
 TemporaryQueue tempQueue;
 QueueSender sender;
 QueueReceiver receiver;

public QueueRequestor(QueueSession session, Queue queue) throws JMSException {
  this.session = session;
  this.queue = queue;
  tempQueue = session.createTemporaryQueue();
  sender = session.createSender(queue);
  receiver = session.createReceiver(tempQueue);
}

public Message request(Message message) throws JMSException {
  message.setJMSReplyTo(tempQueue);
  sender.send(message);
  return receiver.receive();
}

public void close() throws JMSException {
  session.close();
  tempQueue.delete();
}
}

So we can rewrite the above code sample as follows:

QueueConnection c = (QueueConnectionFactory) (new InitialContext().lookup("qcf"));
QueueSession s = c.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue q = (Queue) (new
InitialContext().lookup("requestqueue"));
Message request = s.createTextMessage("1233442342");
request.setJMSReplyTo(s.createTemporaryQueue();
s.createSender(q).send(m);
Message reply = s.createReceiver(q).receive();
c.close();

There's nothing obviously wrong with that, so why does this typically not work when this code is in an EJB?

Transactions

Before we look at the explanation of why the above code doesn't work in an EJB, let's review some basics about application servers and JMS servers. A JMS server is typically a stand alone process. Applications can communicate with this server using a client runtime. The client runtime implements the JMS API as defined in the JMS specification. If you're building a stand-alone application, you use the client runtime directly. Example, when you're using the JMS server shipped with Java CAPS you would be using com.stc.stcjms.jar to talk to the server process (stcms.exe ). The client runtime implemented in com.stc.jms.stcjms.jar implements the JMS api and communicates with the server (stcms.exe ) over tcp/ip sockets.

However, when your code lives in an EJB, you're not using the JMS client runtime directly. For example, when you obtain a connection factory from JNDI in an EJB, you will not get the connection factory object that belongs to the JMS provider itself, but you get a factory that is implemented by a resource adapter. As of J2EE 1.4, the interfacing between the application server and JMS is done through a Resource Adapter. In the example for Java CAPS, you would obtain a connection factory implemented by the JMSJCA resource adapter instead of the com.stc.jms.client.STCQueueConnectionFactory that belongs to the STCMS client runtime.

The Resource Adapter acts like a wrapper around the objects that are implemented in the JMS client runtime. One of the responsibilities of the Resource Adapter is to make sure that JMS acts like a transactional resource. This means that JMS connection (technically, the JMS session) is enlisted with the transaction manager of the applicaiton server. In fact, under the covers the Resource Adapter really uses an XASession rather than an AUTO_ACKNOWLEDGE session. What effect did it have to specify the arguments to

c.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

In fact, these arguments are ignored, so they have no effect whatsoever.

Use of JMS in a transaction

Let's say that we have a bean managed EJB (BMT) with the following code:

EJBContext ctx = ...;
ctx.getUserTransaction().begin();

QueueConnection c = (QueueConnectionFactory) (new InitialContext().lookup("qcf")); QueueSession s = c.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); Queue q = (Queue) (new InitialContext().lookup("requestqueue")); Message request = s.createTextMessage("1233442342"); request.setJMSReplyTo(s.createTemporaryQueue(); s.createSender(q).send(m); Message reply = s.createReceiver(q).receive(); c.close(); ctx.getUserTransaction().commit();

The message will be sent to the queue only when the statement getUserTransaction().commit() is executed. Before this statement, the message will not be visible anywhere.  So when the receive() method is executed, the message was really not sent to the queue. The other process never gets the request, and hence, a reply will never be received. Since no timeout is specified, the application simply hangs.

Now that we know that, the solution is pretty simple: just commit the transaction before receiving the reply.

ctx.getUserTransaction().begin();

QueueConnection c = (QueueConnectionFactory) (new InitialContext().lookup("qcf")); QueueSession s = c.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); Queue q = (Queue) (new InitialContext().lookup("requestqueue")); Message request = s.createTextMessage("1233442342"); request.setJMSReplyTo(s.createTemporaryQueue(); s.createSender(q).send(m); ctx.getUserTransaction().commit();
ctx.getUserTransaction().begin(); Message reply = s.createReceiver(q).receive(); c.close(); ctx.getUserTransaction().commit();

It depends on the business logic if you can simply split up the transaction in two. Perhaps something else happens in the transaction, for example you could update a database in the same transaction. In that case, you may not want to commit the transaction yet! How can we deal with that situation?

The solution would be to suspend the transaction, create a new transaction, send the request, commit the new transaction, unsuspend the first transaction, and continue. Sounds complicated? It is, but it can be simplified using an EJB: simply add a new business method to the EJB, make sure that the method's trans-attribute is set to NotSupported .
Next, instantiate a new EJB and call that method. By calling that method, the transaction will automatically be suspended.

Note that it is crucial to instantiate a new EJB: if you just call the new business method from the same EJB instance, the application server will not intercept the method and will not suspend the transaction.

When the EJB is CMT

Pretty much the same goes if the EJB is not BMT but CMT. If a transaction was started, the same deadlock occurs: the send() is never committed until the transaction is committed, so the receive() method will never receive a reply. In that case, add a new business method and make sure that the trans-attribute is set to RequiresNew . The application server will then suspend the main transaction and start a new transaction automatically. As with BMT, make sure that you call the new business method on a new instance of the EJB rather than on the same instance.

When there is no transaction

What happens if there is no transaction when you call send() ? Well, that depends on the implementation. The specification is not clear on what should happen when an XA resource is used outside of a transaction. The JMS CTS implies that the behavior should be that of auto-commit (or AUTO_ACKNOWLEDGE in JMS parlance). CTS stands for Compliance Test Suite. The CTS for JMS consists of a few thousand tests. Every implementation that claims that  be compliant with the JMS spec must pass these tests. Hence, most implementations, including the one that ships with Java CAPS will exhibit AUTO_ACKNOWLEDGE behavior. In that case, the above code will work and the QueueRequestor can be used.

What about the QueueRequestor?

Can't we use the QueueRequestor with transactions? The simple answer is no. Why did the Resource Adapter not take care of this? The problem is that the QueueRequestor is implemented by the JMS api, and not by the JMS provider or Resource Adapter. Perhaps this is another example why it is generally a bad idea to provide implementation classes in API-s?

There's another problem with the QueueRequestor class: there is no way to specify a timeout value. You would probably prefer a timeout exception rather than having a hanging application.

Conclusion

Knowing what happens under the covers explains why request/reply cannot work in a single transaction. Simply post the request in a separate transaction.

分享到:
评论

相关推荐

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part3

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part3

    ejb3_structs

    【 ejb3_structs 】是一个关于企业级JavaBean(EJB)3.0与Struts框架集成的项目。在Java EE(Enterprise Edition)应用程序开发中,EJB3和Struts是两个重要的组件,分别负责业务逻辑管理和用户界面展示。 **EJB...

    EJB3的三本好书第3本 Mastering EJB3 4ed

    1. Beginning EJB3 Application Development From Novice to Professional联接http://download.csdn.net/source/1865607 2. EJB3 in Action 2007联接http://download.csdn.net/source/1865636 3. Mastering EJB3联接...

    EJB TO JMS

    【EJB to JMS:理解消息传递与JMS Topic】 在企业级Java应用程序中,有时我们需要在不同组件之间传递信息,而无需它们直接相互依赖。这就是Java消息服务(Java Message Service,简称JMS)发挥作用的地方。JMS允许...

    EJB3的三本好书第2本, EJB3 in Action 2007

    1. Beginning EJB3 Application Development From Novice to Professional联接http://download.csdn.net/source/1865607 2. EJB3 in Action 2007联接http://download.csdn.net/source/1865636 3. Mastering EJB3联接...

    EJB3的三本好书第1本 Beginning EJB3 Application Development From Novice to Professional

    1. Beginning EJB3 Application Development From Novice to Professional联接http://download.csdn.net/source/1865607 2. EJB3 in Action 2007联接http://download.csdn.net/source/1865636 3. Mastering EJB3联接...

    JMS的使用范例,EJB初学者必看

    本源代码主要向EJB初学者介绍JMS的使用,如何创建和初始化一个请求Requestor对象、如何初始化请求Requestor对象、如何建立一个会话和发送请求、如何打印出请求的过程信息、如何接受由反馈者replier发回的消息等。

    jms-ejb3 source code

    《JMS与EJB3整合应用的源代码解析》 在Java世界中,JMS(Java Message Service)和EJB3(Enterprise JavaBeans 3.0)是两个至关重要的技术,它们分别负责消息传递和企业级应用的组件模型。本资料主要探讨的是将JMS...

    EJB中用JMS模拟多线程机制的设计和实现

    EJB中用JMS模拟多线程机制的设计和实现 作者:高燕 李旭伟 文震 来源:工业技术 / 自动化技术、计算机技术收藏本文章 多线程机制是提高系统执行效率的关键,但对于采用EJB技术的服务器端,由于EJB规范限制使用多线程...

    JPA/JBOSS/EJB基础教程PDF资料

    EJB 3是EJB规范的一个重大革新,显著简化了EJB的使用。在EJB 3中,开发者可以通过注解(Annotations)而不是XML配置文件来声明bean的属性和行为。实体bean从EJB 2.x的复杂接口转换为简单的POJO(Plain Old Java ...

    tomcat 调用weblogic EJB

    - **WebLogic**:一款由Oracle公司提供的功能强大的企业级Java应用服务器,支持EJB、JMS等标准。 - **EJB**:Enterprise JavaBeans,一种用于开发分布式企业级应用程序的标准组件模型。 #### 实现步骤 1. **配置...

    B-S结构与C-S结构的EJB编写实例代码

    EJB可以实现会话bean(处理客户端会话)、实体bean(代表数据库中的持久化对象)和消息驱动bean(处理JMS消息)。在B/S架构中,EJB通常与Servlet和JSP结合,处理Web请求并调用业务逻辑;而在C/S架构中,EJB可能被...

    达内PPT/ j2ee&ejb课件

    EJB(Enterprise JavaBeans)是Java EE平台的核心组成部分,主要用于构建可复用的服务器端组件。本节我们将深入探讨这两个关键概念及其在实际开发中的应用。 首先,让我们了解一下Java EE。Java EE是Oracle公司提供...

    CORBA Client/Server调用EJB开发说明,带有例子学习

    CORBA (Common Object Request Broker Architecture) 是一种中间件技术,用于构建分布式系统,它允许不同平台和编程语言的组件之间进行交互。EJB (Enterprise JavaBeans) 是Java平台上的企业级组件模型,用于构建可...

    实战EJB 实战EJB 实战EJB

    ### 实战EJB知识点解析 ...EJB2.0支持与JMS的集成,本节将介绍如何在EJB组件中使用JMS进行消息传递。 通过上述章节的学习,开发者可以全面掌握EJB的基本概念和技术细节,为实际项目开发打下坚实的基础。

    基于MVC设计模式构筑JSPServlet+EJB的Web应用

    ### 基于MVC设计模式构筑JSP/Servlet+EJB的Web应用 #### 概述 随着互联网的快速发展,Web应用已经从简单的静态内容发布转变为支持动态交互内容的处理,例如在线娱乐、电子商务等。为了满足这些变化,Web应用程序...

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part2

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part2

    Java EJB简单例子.rar

    Java EJB简单例子,这是HelloWorldBean的Home接口,它是EJB对象的生成库,无状态会话(将在下一个实例中具体讲解)Bean,这个接口是客户端与EJB对象相互作用的中间途径,通过Client触发调用Bean方法:  try {  //...

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part1

    经典Java EE企业应用实战:基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 part1

    JMS 介绍及其在 EJB 2.0 中的用法

    总的来说,JMS在EJB 2.0中的使用扩展了企业级Java应用的能力,使得应用程序可以更有效地处理异步通信和解耦。通过MessageDrivenBean,开发者能够构建出更健壮、可扩展的系统,而不需要直接处理底层的消息传递细节。

Global site tag (gtag.js) - Google Analytics