如果发生运行时异常,则不会重新执行EJB计时器

时间:2014-12-01 11:11:57

标签: java java-ee timer transactions ejb

假设我们有这个课程:

@Startup
@Singleton(mappedName = "workflow", name = "workflow")
@ConcurrencyManagement(value = ConcurrencyManagementType.BEAN)
public class Workflow implements WorkflowInterfaceLocal, WorkflowInterfaceRemote {
    private Object msg;

    public <T> Future<Object> doTask(T msg) {
        for(int i=0; i<10;i++) {
            try {
                Thread.sleep(1000);
                if(i==5) {
                    throw new RuntimeException("a");
                }
                System.out.println("DOTASK" + (i+1));
            } catch(InterruptedException ex) {
                //e
            }
        }
    }

    public void setTimer(long intervalDuration) {
        LOG.debug("Setting a programmatic timeout for " + intervalDuration
                + " milliseconds from now.");

        timerService.createTimer(intervalDuration, null);
    }

    @Timeout
    private void executeOnTimeout(Timer t){
        LOG.debug("start -  doTask with timer timeout");
        t.cancel(); //execute one time and cancel the timer
        doTask(this.msg);
        LOG.debug("end - doTask with timer timeout");

    }
}

这个类应该异步调用executeOnTimeout,这将调用doTask方法。

这就是我所说的:

WorkflowInterfaceRemote workflow = lookupRemoteEJB(WorkflowInterfaceRemote.class);
workflow.setTimer(0);

它工作正常,方法被成功调用,并且调用方法不会挂起,因此@Timeout带注释的方法被异步调用。

也许您在RuntimeException方法中注意到了doTask。我把它放在那里有一个特定的原因:如果doTask触发(无论出于何种原因)RuntimeException,EJB容器会重新启动它,因此输出为:

[01/12/14 11.49.27:258 CET] 00000017 SystemOut     O DOTASK1
[01/12/14 11.49.28:258 CET] 00000017 SystemOut     O DOTASK2
[01/12/14 11.49.29:258 CET] 00000017 SystemOut     O DOTASK3
[01/12/14 11.49.30:259 CET] 00000017 SystemOut     O DOTASK4
[01/12/14 11.49.31:259 CET] 00000017 SystemOut     O DOTASK5
[01/12/14 11.49.38:274 CET] 00000017 LocalExceptio E [Exception stack trace]

[01/12/14 11.49.33:272 CET] 00000017 SystemOut     O DOTASK1
[01/12/14 11.49.34:273 CET] 00000017 SystemOut     O DOTASK2
[01/12/14 11.49.35:273 CET] 00000017 SystemOut     O DOTASK3
[01/12/14 11.49.36:274 CET] 00000017 SystemOut     O DOTASK4
[01/12/14 11.49.37:274 CET] 00000017 SystemOut     O DOTASK5
[01/12/14 11.49.38:274 CET] 00000017 LocalExceptio E [Exception stack trace]

我正在阅读this article,这是一篇意大利文章,我将在这里翻译相关部分(名为&#34;计时器e transazioni&#34;)

  

计时器和交易

     

计时器的创建和取消是事务性的。这意味着如果事务在创建或取消计时器后执行回滚,则其创建或取消将中止。此外,考虑到定时器是异步的,没有事务传播,而是在调用回调方法时创建新事务(就好像它具有REQUIRES_NEW属性)。 如果此事务失败或执行回滚,容器会尝试至少执行一次。

现在,如果我正确理解它,如果@Timeout方法触发了一个execption,则事务失败,EJB容器会尝试重新调用该方法。

对于我来说,这是一种不受欢迎的行为,无论我希望@Timeout方法始终执行一次,我都会这样做。

  

为什么不使用@Asynchronous注释而不是@Timeout

因为我使用的是Websphere 8,它具有该注释的已知错误。 IBM表示它已经修复了一些他们发布的修补程序包,但我使用的是最新的修订包,问题仍然存在,所以这不是一个选项。

  

为什么不使用WorkManager代替Work

因为我似乎必须使用IBM的工作管理器实现类,并且我希望尽可能地使我的代码不依赖于服务器。

有没有办法告诉容器不要再尝试执行我的方法?

1 个答案:

答案 0 :(得分:0)

如果业务需求允许您更改事务属性,则可以将doTask()方法的事务属性设置为REQUIRES_NEW,然后在超时回调中通过EJB代理调用此方法。这个想法如下:

@Resource
SessionContext sessionContext;

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public <T> Future<Object> doTask(T msg) {
  ...
}

@Timeout
private void executeOnTimeout(Timer t) {
    LOG.debug("start -  doTask with timer timeout");
    t.cancel(); //execute one time and cancel the timer
    try {
        WorkflowInterfaceLocal proxy = (WorkflowInterfaceLocal) sessionContext.getBusinessObject(WorkflowInterfaceLocal.class);
        doTask(this.msg);
    } catch (Exception e) {
        LOG.error("Exception " + e.getMessage());
    }
    LOG.debug("end - doTask with timer timeout");
}