有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

回滚时的java取消ejb计时器

有没有办法确保在发生异常时取消周期性(每10秒一次)和持久性计时器?@Timeout方法的实现如下(从遗留代码简化):

@Timeout
@TransactionAttribute(REQUIRES_NEW)
public void onTimeout(Timer timer) {
    try {
        doSomeBusinessLogic();
    } catch (Exception e) {
        // throwing this exception makes sure rollback is triggered
        throw new EJBException(e);
    }
}

doSomeBusinessLogic()中出现任何异常时,需要回滚其事务。这很好用。但是,我还要确保计时器被取消

简单的解决方案是将timer.cancel()放在catch块中。但是,这不起作用,因为取消操作也将回滚(JEE6 Turorial):

An enterprise bean usually creates a timer within a transaction. If this transaction is rolled back, the timer creation also is rolled back. Similarly, if a bean cancels a timer within a transaction that gets rolled back, the timer cancellation is rolled back. In this case, the timer’s duration is reset as if the cancellation had never occurred.

如果发生异常/回滚,如何确保取消计时器(防止任何进一步的超时)?设置最大重试次数也足够了,但我认为JBoss不支持这一点

应用服务器是JBossAS7.2


共 (3) 个答案

  1. # 1 楼答案

    我还尝试了Sergey提出的解决方案,似乎效果不错——计时器被取消了。在JBoss EAP 6.2上测试。 以下是我用于测试的代码:

    @Stateless
    public class TimeoutTest implements TimeoutTestLocal {
    
    @Resource
    TimerService timerService;
    
    @Resource
    SessionContext sessionContext;
    
    @Timeout
    @TransactionAttribute(TransactionAttributeType.NEVER)
    public void tmout(javax.ejb.Timer timer) {
        try {
            System.out.println("timout invoked");
            //instead of internal call let's invoke doNothing as
            //this timeout callback is client of TimeoutTest EJB
            //in this way doNothing will be run inside transaction
            TimeoutTestLocal local = sessionContext.getBusinessObject(TimeoutTestLocal.class);
            local.doNothing();  
        } catch (Exception e) {
            timer.cancel();
            System.out.println("Timer cancelled");
        }
    }
    
    @Override 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void doNothing() {
        throw new EJBException("RE Exception");
    }
    
    
    @Override
    public void schedule() {
        timerService.createTimer(5000, 10000, "test");
    }
    }
    
  2. # 2 楼答案

    我有一个类似的情况,但是取消catch子句中的计时器似乎有效。我之所以需要它,是为了强制容器(Wildfly)不要重试失败的超时

    代码如下所示:

    @Timeout
    public void onTimeout(Timer timer) {
        try {
            //the other ejb has @TransactionAttribute(TransactionAttributeType.SUPPORTS)
            doSomeBusinessLogicInSomeOtherEjbThatThrowsEjbException();
        } catch (EJBException e) {
            timer.cancel();
            throw e;//this is not necessary since the EJB context has already getRolledBack = true at this point
        }
    }
    
  3. # 3 楼答案

    1. 您可以从计时器bean创建新的事务(调用下一个EJB)
    2. 您可以更改TA@TransactionAttribute(从不)并从计时器bean创建新的事务(调用下一个EJB)

    然后放上计时器。catch块中的cancel()