Обходной путь для 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
}