【问题标题】:Make EJB timer not re-execute if a runtime exception occurs如果发生运行时异常,使 EJB 计时器不重新执行
【发布时间】:2014-12-01 11:11:56
【问题描述】:

假设我们有这个类:

@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注解的方法被异步调用了。

也许你注意到doTask 方法中的RuntimeException。我把它放在那里有一个特定的原因:如果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,这是一篇意大利文,我将在这里翻译相关部分(名为“Timer e transazioni”):

定时器和事务

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

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

这对我来说是一种不受欢迎的行为,无论有什么异常,我都希望始终执行一次 @Timeout 方法。

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

因为我使用的是 Websphere 8,该注释存在一个已知错误。 IBM 说它在他们发布的一些修复包中得到了修复,但我使用的是最新的修复包,问题仍然存在,所以这不是一个选项。

为什么不使用 WorkManagerWork 来代替?

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

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

【问题讨论】:

  • 出于好奇:为什么要为无状态 bean 指定 @ConcurrencyManagement 注释?此注释适用于单例 bean。
  • @slwk 好问题。我忘了包括单例注释。实际上,这是从我的代码中提取的示例,其中 Workflow 类扩展了 CommonWorkflow 类,它是一个单例 bean。我包括它。

标签: java jakarta-ee timer transactions ejb


【解决方案1】:

如果您的业务需求允许您更改事务属性,您可以将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");
}

【讨论】:

  • 从问题中链接的文章中了解到,默认情况下会创建一个新事务,手动创建一个有什么区别?
  • 当您拥有具有默认事务属性的doTask() 方法 - REQUIRED 时,它将继续在超时回调中启动的事务。当您获得系统异常时,事务将回滚并再次调用超时回调。但是,当您通过代理调用doTask()REQUIRES_NEW 时,在系统异常的情况下,只有为doTask() 创建的事务将被回滚。与超时回调关联的事务是不同的事务,因此不会被标记为回滚,也不会再次触发超时。
猜你喜欢
  • 2017-08-20
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 2017-09-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多