Иметь задачи расписания EJB с синтаксисом crontab

Я пытаюсь выяснить, какие возможности у меня есть для решения следующей проблемы.

a) Я хочу иметь таблицу базы данных, которая использует «синтаксис crontab» для планирования задач, структура будет примерно такой:

    |-Id-|---Crontab Syntax---|---------Task----------|
    | 1  | 30 *  *  *  *    * | MyClass.TaskA(args[]) |
    | 2  | 0  1  *  *  1-5  * | MyClass.TaskB(args[]) |
    |    |                    |                       |

Приведенная выше таблица может быть изменена в любое время внешним приложением. Добавленные или удаленные задачи должны мгновенно влиять на планировщик.

б) Сам планировщик должен находиться на сервере приложений Java. Он должен постоянно синхронизироваться с активными запланированными задачами в таблице базы данных. Всякий раз, когда происходит запланированное событие, оно должно запускать/вызывать EJB со значением в «Задаче» в качестве аргумента.

Я не ищу ответа на вышеуказанную проблему. Вместо этого необходимо указать, какие фреймворки можно использовать для разбора crontab и каким образом должен быть развернут компонент EJB, представляющий планировщик.

Заранее спасибо.


person aksamit    schedule 09.09.2011    source источник


Ответы (3)


См. @Schedule API EJB 3.1. API, который мы выбрали для спецификации, немного ближе к синтаксису Quartz, чем cron — разница между ними небольшая.

Вот пример аннотации:

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 против ScheduleExpression

  • @Schedule
    • Statically configured
    • Возможны многие методы расписания
    • Невозможно передать аргументы
    • Не может быть отменен

Все вышеперечисленное выполняется в дескрипторе развертывания и поэтому ограничено только тем, что можно настроить заранее. Более динамичная версия использует следующую подпись TimerService:

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

  • ScheduleExpression
    • Dynamically created
    • Ровно один @Timeout поддерживает все ScheduleExpression
    • Метод тайм-аута должен принимать javax.ejb.Timer в качестве параметра.
    • Arguments can be passed
    • Может быть отменен вызывающим абонентом или методом @Timeout.

Также обратите внимание, что существует аннотация перехватчика @AroundTimeout, которая работает идентично @AroundInvoke и позволяет перехватчикам участвовать в функциях таймера компонента.

person David Blevins    schedule 09.09.2011
comment
Спасибо за ваш очень описательный ответ. Я немного поэкспериментировал с Schedule-API. Однако одно ограничение, которое я вижу в этом подходе, заключается в том, что я не могу запланировать событие для общего метода с конкретным аргументом. Таким образом, я был бы ограничен возможностью планировать только предопределенные методы, или вы видите возможность, которую я упустил? - person aksamit; 19.09.2011
comment
API ScheduleExpression именно таков: один общий метод с конкретными аргументами для каждого расписания. Я обновил ответ, чтобы попытаться выделить это немного больше. - person David Blevins; 20.09.2011
comment
Спасибо. Решил мою проблему, не добавляя Quartz (что тоже решило бы задачу) - person aksamit; 20.09.2011
comment
все же было бы неплохо, если бы появилось новое ScheduleExpression(0 1 * * 1-5 * /*crontabLine*/) - person weberjn; 28.11.2017

EJB имеет собственные встроенные таймеры, но вам придется написать шаблонный код для перевода синтаксического анализа cron. Разбор самих инструкций cron должен быть тривиальным.

Если вы не боитесь выходить за пределы EJB, Quartz, как упоминается в lexicore, является отличным вариантом.

person mikek    schedule 09.09.2011

Взгляните на Quartz. Если вы используете Spring, там очень хорошая поддержка. Аккуратная, надежная, хорошо работающая вещь.

person lexicore    schedule 09.09.2011