EJB中的@Schedule方法在异常后没有执行

时间:2011-04-11 08:50:50

标签: ejb java-ee-6

我在Java EE6 Singleton Session Bean中有一个方法,每隔30秒由一个@Schedule(...)计时器调用。这可以正常工作,直到在方法中抛出异常并捕获异常(异常是在try-catch块中抛出并捕获)。异常发生后,计时器停止调用该方法。

无论是否发生异常,我如何强制计时器再次调用该方法?

致以最诚挚的问候,

基督教

6 个答案:

答案 0 :(得分:12)

相关讨论见http://www.java.net/node/706287

事实证明,如果在@Schedule方法中抛出异常,则该方法将在5秒后再次调用,如果失败,则定时器只是DIES。永远不再打电话了。

如果你是这样的那就是经常想做一些监督。

所以我的解决方案是将所有代码都包含在try块中的@Schedule方法中并捕获所有异常,记录它们,然后返回就像一切正常一样。

如果有一种更优雅的方式,我很乐意听到它。

编辑:不,只是添加一个try {...} catch(例外e){...}不是一个不漏水的解决方案。有时我还是会死了@Schedule。当数据库事务出现问题时,JEE容器可能会做一些模糊的事情吗? Nayan:你能澄清一下你在这件事上说的话吗?

答案 1 :(得分:5)

如文档中所述:

  • 可选的持久元素采用布尔值,用于指定自动计时器是否应在服务器重新启动或崩溃后继续存在。默认情况下,所有自动计时器都是持久的。

  • 企业bean通常在事务中创建一个计时器。如果回滚此事务,则还会回滚计时器创建。类似地,如果bean取消了回滚的事务中的计时器,则回滚计时器取消。在这种情况下,计时器的持续时间将被重置,就好像取消从未发生过一样。

如果计时器在遇到异常后失败,那么剩下的可能方法是在catch块中手动创建一个新的计时器。

答案 2 :(得分:3)

如果您碰巧使用Glassfish应用程序服务器(我不确定它是否首先是服务器特定的),请查看问题Avoid expunging timer on glassfish的以下答案。它对我帮助很大。

编辑(死链接时的说明)

在链接的答案中,作者Carlo Pellegrini解释说问题是在schedueled任务的事务中发生了异常。如果可以将业务逻辑外包到另一个在另一个事务(@TransactionAttribute(REQUIRES_NEW)中执行)的EJB中,问题就会消失。此外

  

这有以下好处:

     
      
  • 不在容器启动的事务中抛出异常(从而避免删除)
  •   
  • 更好地记录例外
  •   
  • 明确划分“真正的”商业交易
  •   

答案 3 :(得分:1)

我想,我有解决这个问题的方法。我的问题也是,如果发生EJB / JPA错误,则删除了计时器。

我刚尝试用CDI事件来解决这个问题,因为我想要异常,可能会被抛到另一个地方,所以@Schedule没有被触及。我认为使用cdi事件会将@SchedulestartSomeEvent - 方法分离。它不是。 (我不知道,为什么编程有时候是这样一个石器时代的科学)但是诀窍是@Asynchronous - 注释startSomeEvent - 方法。

所以,我已经实现了@Singleton TimerService,它用于我的应用程序中的所有计时器:

@Singleton
public class TimerService implements Serializable {

    @Inject
    private Event<SomeEvent> someEvent;

    @Schedule(hour = "*", second = "0", minute = "*", persistent = false)
    public void fireSomeEvent() {
        someEvent.fire(new SomeEvent());
    }

}

SomeEvent - 事件由方法观察,实际上正在做你想要的东西。在示例中,它创建了一个Person,其中包含usernameusernameUNIQUE,因此第二次触发事件时会抛出MySQLIntegrityConstraintViolationException: Duplicate entry 'uniqueUsername' for key 'USERNAME'。这会导致我们@Schedule被删除,但事实并非如此,因为我们有@Asynchronous - 注释。

@Singleton
public class TimerTestService {

    @PersistenceContext(unitName = "VWMam3PU")
    private EntityManager entityManager;

    @Asynchronous
    public void startSomeEvent(@Observes SomeEvent event) {
        Person p = new Person();
        p.setUsername("uniqueUsername");
        entityManager.persist(p);
    }

}

SomeEvent - 类是一个简单的POJO:

public class SomeEvent {

}

所以,我一直在寻找解决方案。现在它对我有用。它会引发异常并一次又一次地尝试。

答案 4 :(得分:0)

由于我无法评论Per Lindberg关于水密异常解决方案的上一篇文章,我将在此解释一个可能的解决方案。

我有一个类似的问题。我有一个我的缓存的日程安排更新。但是,附表不应因任何例外而死亡。因为每次发生错误时都需要重新启动应用程序才能再次启用调度程序。

在我的情况下可以解决这个问题,即使用新事务将容器事务与数据库调用分开。您可以处理单独事务中的所有异常,并且只转发您预期/可以在计划方法中处理的那些异常。

以下是我发现的一个使用接口描述解决方案的示例。它没有一个也有效。在我的情况下,我只是再次注入Singleton bean并调用它。

https://web.archive.org/web/20160511085332/http://www.javahelp.info:80/2009/11/01/using-transactionattribute-in-submethods-on-same-ejb3-beans/

答案 5 :(得分:0)

一种可能的解决方案是使用ManagedScheduledExecutorService

@Stateless
public class MyService {

    @Schedule(hour = "*", minute = "*")
    public void doSome() {
        final Future<?> future = executorService.submit(() -> {
            try {
                doActually();
            } catch (final Exception e) {
                // log it out
            }
        });
    }

    @Resource
    private ManagedScheduledExecutorService executorService;
}
相关问题