繁体   English   中英

捕获消息驱动Bean(MDB)中的异常

[英]Catch Exceptions inside a Message Driven Bean (MDB)

我该如何处理mdb中的异常? 我有一种有趣的感觉,异常发生在try catch块之后,所以我无法捕获并记录它。 Glassfish v3决定重复整个信息。 它会运行到一个无限循环中,并在硬盘上写入很多日志文件。

我正在使用Glassfishv3.01 + Eclipselink 2.0.1

public class SaveAdMessageDrivenBean implements MessageListener {

    @PersistenceContext(unitName="QIS") 
    private EntityManager em;

    @Resource
    private MessageDrivenContext mdc;

    public void onMessage(Message message) {
        try {
            if (message instanceof ObjectMessage) {
                ObjectMessage obj = (ObjectMessage)message;
                AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
                save(alyzres);
            }
        } catch (Throwable e) { 
            mdc.setRollbackOnly();
            log.log(Level.SEVERE, e);
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {

       Some s = em.find(Some.class, somepk);
       s.setSomeField("newvalue");

       // SQL Exception happens after leaving this method because of missing field for ex.
    }
}    

你有一个消息中毒的坏情况......

我看到的主要问题是:

  • 你在onMessage()直接调用save()方法:这意味着容器没有办法在save方法周围注入正确的事务处理代理
  • 在任何情况下, save()方法都应该有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)以便在单独的事务中提交,否则它将加入onMessage事务(默认为REQUIRED )并绕过你的异常处理代码,beign后提交成功执行onMessage

我要做的是:

save方法移动到新的无状态会话bean:

@Stateless
public class AnalyzerResultSaver
{
    @PersistenceContext(unitName="QIS") 
    private EntityManager em;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
        Some s = em.find(Some.class, somepk);
        s.setSomeField("newvalue");
        // SQL Exception happens after leaving this method
    }
}

在MDB中注入此bean:

public class SaveAdMessageDrivenBean implements MessageListener {

    @Inject  
    private AnalyzerResultSaver saver;

    @Resource
    private MessageDrivenContext mdc;

    public void onMessage(Message message) {
        try {
            if (message instanceof ObjectMessage) {
                ObjectMessage obj = (ObjectMessage)message;
                AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
                saver.save(alyzres);
            }
        } catch (Throwable e) { 
            mdc.setRollbackOnly();
            log.log(Level.SEVERE, e);
        }
    }
}

另一个提示:在此代码中,消息中毒仍然存在。 现在它派生自调用mdc.setRollbackOnly();的行mdc.setRollbackOnly();

我建议在这里记录异常并将消息传送到毒性队列,从而阻止容器无限制地重新提交消息。

更新:

“毒性队列”或“错误队列”只是保证您的(希望可恢复的)丢弃的消息不会完全丢失的意思。 它在集成场景中大量使用,其中无法保证消息数据的正确性。

设置中毒队列意味着定义目标队列或主题,并将“错误”消息重新发送到此目标。

操作员应定期检查此队列(通过专用应用程序)并修改消息并重新提交到“正常”队列,或丢弃该消息并要求重新发送。

我相信你发布的代码大多没问题。

你的用途

    @TransactionAttribute(TransactionAttributeType.REQUIRED)

完全被忽略,因为此(和大多数其他)注释只能应用于业务方法(包括onMessage)。 这无关紧要,因为你的onMessage方法免费得到一个隐含的方法。

这导致消息处理在Java EE容器中是事务性的。 如果事务因任何原因失败,则需要容器尝试再次传递消息。

现在,您的代码正在从save方法中捕获异常,这很好。 但是,您明确标记了事务以进行回滚。 这具有告诉容器消息传递失败并且应该再次尝试的效果。

因此,如果您删除:

    mdc.setRollbackOnly();

容器将停止尝试重新传递消息。

如果我没弄错的话,你就让容器处理交易。 这样,实体管理器将对方法完成后将刷新的操作进行排队,这就是您在方法完成后遇到异常的原因。

直接使用em.flush()作为方法的最后一步将执行事务的所有相关查询,抛出异常,而不是在稍后当容器在提交事务时进行flush()时抛出异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM