2

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

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

这是幸福的道路(从无状态会话 bean 开始):

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

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

//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 中并且它运行正确......但我需要它在一个单独的类中工作以供应用程序的其他部分使用。

4

1 回答 1

1

我认为你有两个问题:

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

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

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

嘿比尔;

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

如果您希望EmailQueueMessenger的行为符合您在其上放置的注释(事务性、注入等),则必须将其作为 EJB 引用,而不是简单的 pojo。因此,您的外部会话 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 不太熟悉,但似乎有一个带有开关的配置屏幕,可让您指定对连接工厂的事务支持级别

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

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

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

我希望这能解决问题,我真的希望它能奏效!

于 2012-05-03T22:01:31.560 回答