当JMS Prod位于帮助程序POJO类中时,如何在事务中包含JMS生产者

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

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

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

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

下面的代码工作,它只是不会在调用类中回滚数据库“persist”如果我强制说错误,例如,上下文查找。 顺便说一句,我也无法让@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中并且它正确运行…但我需要这个在单独的类中工作以供应用程序的其他部分使用。

我认为你有两个问题:

  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实现之间的差异)。

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