Обходной путь для CronSequenceGenerator Последний день месяца?

Итак, вот оно, я хочу запланировать запуск задачи в последний день каждого месяца в 10:10. Мое выражение cron

0 10 10 L * ?

Теперь проблема в том, что CronSequenceGenerator генерирует NumberFormatException для значения 'L'. Это означает, что CronSequenceGenerator в Spring не поддерживает такого рода выражения. Как это сделать любым другим способом (обходной путь). Я не хочу использовать кварц или Spring поддержу это в новых выпусках.

Вот полная трассировка стека:

Exception in thread "main" java.lang.NumberFormatException: For input string: "L"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:492)
    at java.lang.Integer.valueOf(Integer.java:582)
    at org.springframework.scheduling.support.CronSequenceGenerator.getRange(CronSequenceGenerator.java:324)
    at org.springframework.scheduling.support.CronSequenceGenerator.setNumberHits(CronSequenceGenerator.java:297)
    at org.springframework.scheduling.support.CronSequenceGenerator.setDays(CronSequenceGenerator.java:275)
    at org.springframework.scheduling.support.CronSequenceGenerator.setDaysOfMonth(CronSequenceGenerator.java:266)
    at org.springframework.scheduling.support.CronSequenceGenerator.parse(CronSequenceGenerator.java:239)
    at org.springframework.scheduling.support.CronSequenceGenerator.<init>(CronSequenceGenerator.java:81)
    at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:54)
    at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:44)
    at com.hcdc.coedp.datantar.scheduler.SchedulerUtil.start(SchedulerUtil.java:75)
    at com.hcdc.coedp.datantar.scheduler.SchedulerUtil.changeTrigger(SchedulerUtil.java:106)
    at com.hcdc.coedp.datantar.scheduler.SchedulingService.scheduleTransfer(SchedulingService.java:70)
    at com.hcdc.coedp.datantar.scheduler.Scheduler.schedule(Scheduler.java:107)
    at main.Main.main(Main.java:47)

Обновить:

Ниже приведен мой метод планирования

 /**
    * Schedule a task {@link Task} with a specified cron expression.
    * @param task {@link Task}
    * @param cronExpression cron expression to be applied must be a vaild one.
    * @param taskName
    * @return 
    */
     public String start(Task task, String cronExpression, String taskName) {
        CronTrigger trigger = new CronTrigger(cronExpression);//line 2

        CronSequenceGenerator generator = new CronSequenceGenerator(cronExpression, TimeZone.getTimeZone("GMT+5:30"));
        List<Date> dateList = new ArrayList<>(5);
        Date currentDate = new Date();
        for (int i = 0; i < 5; i++) {
            currentDate = generator.next(currentDate);
            dateList.add((currentDate));
            System.out.println("Next Exceution times are" + currentDate);
        }
        ScheduledFuture sf = tps.schedule(task, trigger);

        //TODO Save this scheduled future with a specific task name.
        ContextHolder.schduledFutureMap.put(taskName, sf);
        return cronExpression;
    }

И в строке 2 он выдает NumberFormatException, когда я передаю указанное выражение cron.

3 ответа

Решение

Эта функция не в стандартном синтаксисе выражения cron. Так что, вероятно, Spring никогда не осуществит это. Глядя на код, я не вижу ни одного хирургического решения, расширяющего CronSequenceGenerator, Так почему же вы просто не используете Quartz, так как это особая функция?

В зависимости от ваших потребностей, вы можете реализовать свой собственный триггер. Что-то вроде:

import java.util.Date;

import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;

public class LastDayOfMonthTrigger implements Trigger {

    private final LocalTime time;

    public LastDayOfMonthTrigger(LocalTime time) {
        this.time = time;
    }

    @Override
    public Date nextExecutionTime(TriggerContext ctx) {
        Date last = ctx.lastScheduledExecutionTime();
        LocalDate date = last == null ? new LocalDate() : new LocalDate(last).plusDays(1);
        LocalDate lastDay = date.dayOfMonth().withMaximumValue();
        return lastDay.toDateTime(time).toDate();
    }
}

В качестве обходного пути я бы запланировал выполнение на все даты

0 10 10 * * ?

и проверил фактическую дату в запланированном методе

public void scheduledTask() {
    Calendar c = Calendar.getInstance();
    if (c.get(Calendar.DATE) == c.getActualMaximum(Calendar.DATE)) {
        ...
    }
}

Оптимизированная версия, которая работает только в последний день месяца:

@Scheduled(cron = "0 55 23 28-31 * ?")
public void doStuffOnLastDayOfMonth() {
    final Calendar c = Calendar.getInstance();
    if (c.get(Calendar.DATE) == c.getActualMaximum(Calendar.DATE)) {
        // do your stuff
    }
}

Есть еще одно решение:

Создайте данные за один месяц. Программа должна запускаться в первый день следующего месяца, чтобы обеспечить сбор всех данных за весь месяц.

import org.apache.commons.lang3.time.DateUtils;

@Scheduled(cron = "0 0 0 1 * ?") // runs on the first day of each month

public void doStuffOnFirstDayOfMonth() {

    Date now = DateUtils.addDays(new Date(), -1); // "now" is now on the last day of the month

}
Другие вопросы по тегам