java - 当 JMS Prod 位于辅助 POJO 类中时,如何在事务中包含 JMS Producer

标签 java exception transactions jms

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

特别是在我正在尝试做的事情的上下文中:我如何在 EJB 的事务中包含一个 POJO JMS 生产者,它在调用 POJO 发送消息之前将一些数据保存在数据库中,这样如果消息异常无法发送,数据库事务也会回滚?我想异步发送邮件。

这是快乐的道路(从无状态 session bean 开始):

  • 将数据保存到数据库//这可行
  • 从持久化的数据中提取选择数据并将其放在 自定义“消息”类(真正的 dto)
  • 调用 EmailQueueMessenger POJO 的 sendEmail 方法,将消息对象传递给它。
  • 消息被发送到 MDB 以处理和发送电子邮件(不是问题的一部分,只是为了完整性)

下面的代码有效,如果我在上下文查找中强制出错,它不会回滚调用类中的数据库“持久化”。顺便说一句,我也无法让@Resource 注入(inject)工作。

//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 定义,但它没有什么区别。

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


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

最佳答案

我认为您有两个问题:

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

  2. 您正在使用 AUTO_ACKNOWLEDGE 创建一个 jms session ,但它需要被处理。此外,请确保 jms 连接来自事务 JCA 源,因为这会将 session 关联到事务。

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

嘿,比尔;

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

如果您希望 EmailQueueMessenger 的行为与您放置在其上的注释一致(事务性、注入(inject)等),您必须将其作为 EJB 引用,而不是作为简单的 pojo。因此,您的外部 session bean 应该如下所示:

@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,但似乎有一个带有开关的配置屏幕,允许您指定 level of transactional support for a connection factory .

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

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

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

我希望一切顺利,我真的希望它能奏效!

关于java - 当 JMS Prod 位于辅助 POJO 类中时,如何在事务中包含 JMS Producer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10438475/

相关文章:

spring - Spring事务中发生死锁时调度程序停止

java - Android 应用 Toast 混淆

java - Maven Shade 包含 slf4j-log4j12,尽管已明确从 POM 中排除

validation - 在函数中使用参数之前,我应该确保参数不为空吗?

android - SqliteConstraintException Android

php - 有没有比事务更高效的方法?

java - 如何为JSF配置web.xml、glassfish-web.xml文件?

java - 如何在Android上实现app子组件的模块化结构?

c++ - 在模板函数上的 try/catch block 后返回

c# - 忽略特定查询的 TransactionScope