【问题标题】:Have an EJB schedule tasks with "crontab syntax"使用“crontab 语法”来安排 EJB 任务
【发布时间】:2011-11-13 18:47:22
【问题描述】:

我正在尝试找出解决以下问题的可能性。

a) 我想有一个使用“crontab 语法”来安排任务的数据库表,结构应该是这样的:

|-Id-|---Crontab 语法---|---------任务----------| | 1 | 30 * * * * * | MyClass.TaskA(args[]) | | 2 | 0 1 * * 1-5 * | MyClass.TaskB(args[]) | | | | |

上表将随时被外部应用程序修改。添加或删除的任务应立即影响调度程序。

b) 调度程序本身应该驻留在 Java 应用服务器上。它应该不断地与数据库表中的活动计划任务同步。每当调度事件发生时,它应该以“Task”中的值作为参数触发/调用 EJB。

我不是在寻找上述问题的答案。而是关于哪些框架可用于 crontab 解析以及应以何种方式部署表示调度程序的 EJB 的一些输入。

提前致谢。

【问题讨论】:

    标签: java jakarta-ee ejb crontab schedule


    【解决方案1】:

    请参阅 EJB 3.1 @Schedule API。我们为规范选择的 API 比 cron 更接近 Quartz 语法——两者之间的差异很小。

    这是一个注释示例:

    package org.superbiz.corn;
    
    import javax.ejb.Lock;
    import javax.ejb.LockType;
    import javax.ejb.Schedule;
    import javax.ejb.Schedules;
    import javax.ejb.Singleton;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * This is where we schedule all of Farmer Brown's corn jobs
     */
    @Singleton
    @Lock(LockType.READ) // allows timers to execute in parallel
    public class FarmerBrown {
    
        private final AtomicInteger checks = new AtomicInteger();
    
        @Schedules({
                @Schedule(month = "5", dayOfMonth = "20-Last", minute = "0", hour = "8"),
                @Schedule(month = "6", dayOfMonth = "1-10", minute = "0", hour = "8")
        })
        private void plantTheCorn() {
            // Dig out the planter!!!
        }
    
        @Schedules({
                @Schedule(month = "9", dayOfMonth = "20-Last", minute = "0", hour = "8"),
                @Schedule(month = "10", dayOfMonth = "1-10", minute = "0", hour = "8")
        })
        private void harvestTheCorn() {
            // Dig out the combine!!!
        }
    
        @Schedule(second = "*", minute = "*", hour = "*")
        private void checkOnTheDaughters() {
            checks.incrementAndGet();
        }
    
        public int getChecks() {
            return checks.get();
        }
    }
    

    here的完整来源

    您可以通过 ScheduleExpression 类以编程方式执行相同的操作,该类只是上述注释的可构造版本。如果计划是在代码中完成的,上面的示例如下所示:

    package org.superbiz.corn;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.Resource;
    import javax.ejb.Lock;
    import javax.ejb.LockType;
    import javax.ejb.ScheduleExpression;
    import javax.ejb.Singleton;
    import javax.ejb.Startup;
    import javax.ejb.Timeout;
    import javax.ejb.Timer;
    import javax.ejb.TimerConfig;
    import javax.ejb.TimerService;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * This is where we schedule all of Farmer Brown's corn jobs
     *
     * @version $Revision$ $Date$
     */
    @Singleton
    @Lock(LockType.READ) // allows timers to execute in parallel
    @Startup
    public class FarmerBrown {
    
        private final AtomicInteger checks = new AtomicInteger();
    
        @Resource
        private TimerService timerService;
    
        @PostConstruct
        private void construct() {
            final TimerConfig plantTheCorn = new TimerConfig("plantTheCorn", false);
            timerService.createCalendarTimer(new ScheduleExpression().month(5).dayOfMonth("20-Last").minute(0).hour(8), plantTheCorn);
            timerService.createCalendarTimer(new ScheduleExpression().month(6).dayOfMonth("1-10").minute(0).hour(8), plantTheCorn);
    
            final TimerConfig harvestTheCorn = new TimerConfig("harvestTheCorn", false);
            timerService.createCalendarTimer(new ScheduleExpression().month(9).dayOfMonth("20-Last").minute(0).hour(8), harvestTheCorn);
            timerService.createCalendarTimer(new ScheduleExpression().month(10).dayOfMonth("1-10").minute(0).hour(8), harvestTheCorn);
    
            final TimerConfig checkOnTheDaughters = new TimerConfig("checkOnTheDaughters", false);
            timerService.createCalendarTimer(new ScheduleExpression().second("*").minute("*").hour("*"), checkOnTheDaughters);
        }
    
        @Timeout
        public void timeout(Timer timer) {
            if ("plantTheCorn".equals(timer.getInfo())) {
                plantTheCorn();
            } else if ("harvestTheCorn".equals(timer.getInfo())) {
                harvestTheCorn();
            } else if ("checkOnTheDaughters".equals(timer.getInfo())) {
                checkOnTheDaughters();
            }
        }
    
        private void plantTheCorn() {
            // Dig out the planter!!!
        }
    
        private void harvestTheCorn() {
            // Dig out the combine!!!
        }
    
        private void checkOnTheDaughters() {
            checks.incrementAndGet();
        }
    
        public int getChecks() {
            return checks.get();
        }
    }
    

    这个例子的来源是here

    附注,这两个示例都可以在普通 IDE 中运行,并且测试用例使用 EJB 3.1 中新增的 Embeddable EJBContainer API。

    @Schedule 与 ScheduleExpression

    • @Schedule
      • 静态配置
      • 可以使用多种计划方法
      • 无法传递参数
      • 无法取消

    以上都是在部署描述符中完成的,因此仅限于可以提前配置的内容。更动态的版本使用 TimerService 的以下签名:

    TimerService.createCalendarTimer(javax.ejb.ScheduleExpression, javax.ejb.TimerConfig)

    • 调度表达式
      • 动态创建
      • 正好一个@Timeout 支持所有的ScheduleExpression
      • 超时方法必须以javax.ejb.Timer为参数
      • 可以传递参数
      • 可以由调用者或@Timeout 方法取消

    另请注意,有一个拦截器 @AroundTimeout 注释,其功能与 @AroundInvoke 相同,并允许拦截器参与 bean 的计时器功能。

    【讨论】:

    • 感谢您非常详细的回答。我已经用 Schedule-API 做了一些试验。但是,我看到这种方法的一个限制是我无法将事件安排到具有特定参数的通用方法。因此,我将被限制为只能安排预定义的方法,或者您是否看到我忽略的可能性?
    • ScheduleExpression API 就是这样:一个通用方法,每个计划都有特定的参数。我已经更新了答案以尝试更多地强调这一点。
    • 谢谢。解决了我的问题,无需添加 Quartz(这也可以解决任务)
    • 不过,如果有新的 ScheduleExpression("0 1 * * 1-5 *" /*crontabLine*/) 就好了
    【解决方案2】:

    EJB 有自己的内置 timers,但您必须编写样板代码来翻译 cron 解析。对 cron 指令本身的解析应该很简单。

    如果您不怕在 EJB 之外冒险,那么 Quartz 就像 lexicore 提到的那样是一个很好的选择。

    【讨论】:

      【解决方案3】:

      看看Quartz。如果你使用 Spring,那里有很好的支持。一个整洁、可靠的好工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-22
        • 1970-01-01
        • 2011-06-22
        • 1970-01-01
        • 2011-07-21
        • 2015-02-27
        • 2021-05-25
        • 2019-08-28
        相关资源
        最近更新 更多