使用"EJB计划任务"并使用"crontab语法"进行调度任务

7
我正在尝试解决以下问题的可能性:
a)我想要一个使用“crontab语法”来调度任务的数据库表,结构应该是这样的:
|-Id-|---Crontab Syntax---|---------Task----------| | 1 | 30 * * * * * | MyClass.TaskA(args[]) | | 2 | 0 1 * * 1-5 * | MyClass.TaskB(args[]) | | | | |
上述表格将由外部应用程序随时进行修改。添加或删除的任务应立即影响调度程序。
b)调度程序本身应驻留在Java应用服务器上。它应该不断地与数据库表中的活动计划任务同步。每当发生计划事件时,它都应该触发/调用一个具有“Task”值作为参数的EJB。
我不是在寻找上述问题的答案,而是希望了解可以用于crontab解析的框架以及代表调度程序的EJB应如何部署的一些信息。
提前感谢您。
3个回答

28

请查看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();
    }
}

这里提供完整源代码链接

您可以通过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();
    }
}

这个示例的源代码在这里

顺便说一下,这两个示例都可以在普通的IDE中运行,并且具有使用Embeddable EJBContainer API的测试用例,这也是EJB 3.1中的新功能。

@Schedule vs ScheduleExpression

  • @Schedule
    • 静态配置
    • 许多计划方法都是可能的
    • 无法传递参数
    • 不能取消

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

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

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

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


1
感谢您提供详细的答案。我已经尝试了一下 Schedule-API。然而,我发现这种方法的一个限制是我无法将事件安排到一个带有特定参数的通用方法。所以我只能限于安排预定义的方法,您是否看到了我忽略的可能性? - aksamit
ScheduleExpression API就是这样:一个通用方法,每个计划都有特定的参数。我已经更新了答案,试图更加突出这一点。 - David Blevins
谢谢。解决了我的问题,而不必添加Quartz(这也可以解决任务)。 - aksamit
2
不过,如果有一个新的ScheduleExpression("0 1 * * 1-5 *" /crontabLine/)会很好。 - weberjn

0

EJB有自己内置的定时器, 但你需要编写样板代码来解析cron。解析cron指令本身应该很简单。

如果你不害怕离开EJB,Quartz就像lexicore提到的一样是一个很好的选择。


-1

看一下Quartz。如果你使用Spring,那里有非常好的支持。一个整洁、可靠、工作良好的东西。


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