当JMS Prod位于帮助程序POJO类中时,如何在事务中包含JMS生产者 [英] How can I include a JMS Producer in a Transaction when the JMS Prod lives in a helper POJO class

查看:172
本文介绍了当JMS Prod位于帮助程序POJO类中时,如何在事务中包含JMS生产者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简短的问题:是否有办法强制由无状态EJB调用的POJO存在于EJB的上下文中,以便事务和资源注入在POJO中起作用?

The short question: Is there a way to force a POJO called by a stateless EJB to live in the context of the EJB so that transactions and resource injection will work in the POJO?

特别是在我想要做的事情的上下文中:如何在EJB的事务中包含POJO JMS Producer,它在调用POJO发送消息之前将数据库中的某些数据持久化,这样如果由于异常无法发送消息,数据库事务也会回滚吗?我想异步发送邮件。

Specfically in the context of what I am trying to do: how can I include a POJO JMS Producer in the transaction of an EJB that persists some data in a database before calling the POJO to send the message, such that if the message can't be sent due to an exception, the database transaction will roll back too? I want to send the mail asynchronously.

这是一个快乐的路径(从无状态会话bean开始):

This is the happy path (starting within stateless session bean):


  • 将数据保存到数据库//这是有效的

  • 从持久数据中提取数据并将其放入
    自定义'消息'类(实际上是dto)

  • 调用EmailQueueMessenger POJO的sendEmail方法,将消息对象传递给它。

  • 消息被发送到MDB处理并发送电子邮件(不是问题的一部分,只是为了完整性)

  • save data to database // this works
  • pull select data from the data that was persisted and place it in a custom 'message' class (really a dto)
  • call the sendEmail method of the EmailQueueMessenger POJO passing it the message object.
  • message is sent to the MDB to process and send the email (not part of the question, just here for completeness)

下面的代码可以工作,如果我强制说出一个上下文查找错误,它就不会在调用类中回滚数据库persist。顺便说一句,我无法让@Resource注入工作。

The code below works, it just won't roll back the database "persist" in the calling class if I force an error in say, a context lookup. BTW, I can't get @Resource injection to work either.

//In the EJB
EmailQueueMessenger eqm = new EmailQueueMessenger();
eqm.sendEmail(messageObject);
// mailObject will be translated into an email message at the other end of the queue.  

/******************** POJO Below ************/  

public class EmailQueueMessenger implements Serializable {

    // Resource injection doesn't work... using 'lookup' below, which does work.
    //    @Resource(name = "jms/EmailerQueueConnectionFactory")
    //    private ConnectionFactory connectionFactory;
    //    @Resource(name = "jms/EmailerQueue")
    //    private Destination EmailerQueue;

        public EmailQueueMessenger() {
        }

        public void sendEmail(MailMessageDTO theMessage) {

            Context ctx = null;
            try {
                ctx = new InitialContext();
                ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/EmailerQueueConnectionFactory");
                System.out.println("JMS Producer CTX Name In Namespace: " + ctx.getNameInNamespace());
                //Destination EmailerQueue = (Destination) ctx.lookup("jms/ERROR"); // forces exception
                Destination EmailerQueue = (Destination) ctx.lookup("jms/EmailerQueue");  // normal working code

                try {
                    Connection con = connectionFactory.createConnection();
                    Session session = con.createSession(false,
                            Session.AUTO_ACKNOWLEDGE);
                    MessageProducer msgProd = session.createProducer(EmailerQueue);

              ...

我试过添加:

@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless

POJO定义的

但它没有什么区别。

to the POJO definition but it doesn't make a difference.

FWIW我正在为EmailQueueMessenger使用单独的类,因为应用程序的其他部分将需要发送偶尔的电子邮件,因此不希望重复的代码。

FWIW I am using a separate class for the EmailQueueMessenger because there will be other parts of the app that will need to send the occasional email, so don't want to duplicated code.

应该提到我做了一个测试,我把所有的JMS东西都移到了第一个EJB中并且它正确地运行了......但我需要这个在单独的类中工作以供应用程序的其他部分使用。

Should mention that I did a test where I moved all the JMS stuff to within the first EJB and it ran correctly... but I need this to work in a separate class for use by other parts of the app.

推荐答案

我认为你有2个问题:


  1. 你需要让你的pojo成为SLSB。它应该被注入你的jms监听器,而不是直接调用,以便你处理代理引用。它仍然可以作为简单的pojo重用,因为如果未将注释部署在容器中,注释将被忽略。

  1. You need to make your pojo an SLSB. It should be injected into your jms listener, not called directly so that you are dealing with the proxy reference. It can still be reused as a simple pojo since the annotations will be ignored if not deployed in a container.

您正在使用AUTO_ACKNOWLEDGE创建一个jms会话但它需要进行交易。此外,请确保jms连接来自事务性JCA源,因为这会将会话与事务关联。

You are creating a jms session using AUTO_ACKNOWLEDGE but it needs to be transacted. Additionally, make sure the jms connection is coming from a transactional JCA source since that will associate the session to the transaction.

=========更新=========

嘿比尔;

道歉,我认为外部bean由于某种原因是JMS监听器.....无论如何,问题是一样的。

Apologies, I thought the outer bean was a JMS listener for some reason..... Anyways, the issue is the same.

如果您希望 EmailQueueMessenger 按照您在其上添加的注释行事(事务性,注入等)你必须将它作为EJB引用,而不是简单的pojo。因此,您的外部会话bean应如下所示:

If you want EmailQueueMessenger to behave in accordance with the annotations you place on it (transactional, injections etc.) you have to reference it as an EJB, not as a simple pojo. Accordingly, your outer session bean should look like this:

@EJB   // key difference
private EmailQueueMessenger eqm;

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void sendMessage(Object messageObject) {
   eqm.sendEmail(messageObject);
}

现在你的

@Resource(name = "jms/EmailerQueueConnectionFactory")
@Resource(name = "jms/EmailerQueue")

@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless

注释将被兑现。

最后,您的JMS发送方将在调用时注册一个事务,您需要确保事务管理器知道您正在该事务中招募第二个资源管理器(首先是DB,现在是JMS) 。我并不熟悉glassfish,但似乎有一个带开关的配置屏幕允许你指定连接工厂的事务支持级别

Lastly, your JMS sender will be enrolled in a transaction at the point of invocation and you need to make sure that the transaction manager is aware that you are enlisting a second resource manager in the transaction (first the DB , and now JMS). I am not that familiar with glassfish, but it seems there is a configuration screen with a switch that allows you to specify the level of transactional support for a connection factory.

我会将发件人代码更改为:

I would change the sender code to:

Session session = con.createSession(true, Session.SESSION_TRANSACTED);

从技术上讲,您可以在EmailQueueMessenger实例中缓存JMS连接实例。您的代码不应该关闭JMS会话,因为这将在事务完成时处理(尽管我已经看到了JMS / JTA实现之间的差异)。

Technically, you can cache the JMS connection instance in the EmailQueueMessenger instance. Your code should not close the JMS session as this will be handled when the transaction completes (although I have seen variances between JMS/JTA implementations on this point).

我希望清除它,我真的希望它有效!

I hope that clears it up, and I really hope it works !

这篇关于当JMS Prod位于帮助程序POJO类中时,如何在事务中包含JMS生产者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆