避免在Glassfish上清除计时器

24

我有一个被@Schedule注释标记的方法,由容器定期调用。

@Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void myTimerMethod() throws Exception {
    ...
}

在特定条件下,我希望这个方法抛出一个异常来导致正在进行的事务回滚。但如果我这样做超过两次,计时器将被清除并且不会再被调用!

INFO: EJB5119:Expunging timer ['68@@1359143163781@@server@@domain1' 'TimedObject = MyBean' 'Application = My-War' 'BEING_DELIVERED' 'PERIODIC' 'Container ID = 89072805830524936' 'Fri Jan 25 21:49:30 CET 2013' '0' '*/5 # * # * # * # * # * # * # null # null # null # true # myTimerMethod # 0' ] after [2] failed deliveries

我知道我可以使用domain.xml配置定时器重新调度。

<domains>
    ...
    <configs>
        <config>
            ...
            <ejb-container session-store="${com.sun.aas.instanceRoot}/session-store">
               <ejb-timer-service>
                     <property name="reschedule-failed-timer" value="true"></property>
                </ejb-timer-service>
            </ejb-container>
            ...
        </config>
    </configs>
    ...
</domains>

但是我的问题是,我可以在部署应用程序时配置此设置吗?

在以下位置找不到:

glassfish-resources.xml
glassfish-ejb-jar.xml
glassfish-web.xml

也许有一些以编程方式完成它的方法吗?

我将服务器配置文件放在配置文件中而不是配置服务器的原因是,这样我的应用程序可以直接安装到全新的glassfish上。


对于那些在异步环境下苦苦挣扎的人,可以参考这篇文章:https://dev59.com/Ym035IYBdhLWcg3wHsKR#32651548。 - Jin Kwon
2个回答

26

我会采用不同的方法。

与其直接从计划方法中抛出异常,不如尝试引入一层间接性,例如:

...
@Inject RealWorkHere realImplementation;

@Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void myTimerMethod(){
  try{
     realImplementation.myTimerMethodImpl()
  }catch (Exception x){
   // hopefully log it somewhere
  }
}
...

其中RealWorkHere是具有实际实现的bean,如下所示:

@Stateless
public class RealWorkHere{
   @TransactionAttribute(REQUIRES_NEW)
   public void myTimerMethod() throws Exception {

   }
}

这样做的好处是:

  • 不会在容器启动的交易中抛出异常(从而避免被清除)
  • 更好地记录异常信息
  • 清晰地划分“真正”的业务交易

另请参阅


3
根据@Schedule的javadoc文档:超时回调方法不得抛出应用程序异常。 - Andre
1
我尝试过这个(但是保持了相同的slsb方法),问题是计时器仍然被清除。虽然我得到了改进的日志记录等,但这是一个进步。我将尝试将其分离为不同的bean(遵循您的示例到点),但对我来说不明显为什么会有影响。 - Aksel Willgert
1
@AkselWillgert 我使用两个bean作为简单的手段来获得单独的事务,否则如果没有特殊结构地使用两种方法,注释不会在“this”上处理,这将使它们都加入到同一个事务中。 - Carlo Pellegrini
1
我明白了,将它移到另一个类中是一个因素,并解决了我的问题。太棒了,不需要为此进行GlassFish特定的配置! - Aksel Willgert
嗨@Carlo Pellegrini,您的方法在过去几年中完美地为我工作。直到我的(内部)myTimerMethodImpl()超时(EJB5123:回滚超时事务)并抛出RollbackException的那一刻。在这种情况下,使用@Schedule注释注释的(外部)myTimerMethod()方法也超时,并最终导致EJB5119:Expungung计时器。有什么想法如何处理超时? - Filou
2
很遗憾,我所看到的唯一解决方法是通过使用@TransactionTimeout来增加外部方法的超时时间,但如果内部方法正在等待阻塞调用,则超时仍可能被触发。采用异步方式可能是更好的方法。 - Carlo Pellegrini

5
当前的Glassfish版本为4,如果在执行超时回调方法时发生应用程序异常,则会清除计时器。
应用程序异常会导致当前事务回滚。在这种情况下,Glassfish会再次尝试无错误地执行超时回调方法。如果再次回滚,则Glassfish会清除计时器。
我在Glassfish问题跟踪器中提交了一个问题,请求在发生异常时不要清除计时器。Glassfish似乎是唯一在发生应用程序异常时清除计时器的应用服务器。有关详细信息,请参见glassfish #20749: Glassfish expunges timer even if callback method keeps its contract。希望您能为我的问题投票。
我还在EJB规范上提交了一个问题,以澄清EJB容器在此类情况下的行为。有关详细信息,请参见ejb-spec #111: Please clearify the behaviour of an container if an application exception is thrown during the execution of a timer callback method

我没有注意到你在下面的回答,只是将那些确切的链接添加到了Carlo的回答中。感谢你在这方面的努力,Oliver。 - DavidS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接