0

I have a large number of tasks running on cron schedules, using Spring Boot's scheduling libraries. However, I want to cause these schedules to not run on specific days (e.g. a nation strike or a government election).

He have a database containing such calendars, which are kept up-to-date. My intention was to override CronExpression#next() by first running super.next(temporal) then comparing the result on a check against my database and then returning a modified Temporal if the super.next() would result on a run on a public holiday. I would assume, that my CronTrigger would obey the next run according to my overriden CronExpression.

What I cannot figure out is where CronExpression is used? The CronExpression API doc says;

See Also: CronTrigger

The CronTrigger API description says;

Trigger implementation for cron expressions. Wraps a CronExpression.

However, all of this API's constructors use String. Why not CronExpression? So, where is the connection between these two classes? (How can I have CronTigger use an overridden CronExpression?).

The only alternative that I can see is that I create abstract class for Runnable, then have it's run() call my database. After which, I re-code my existing tasks to extend this abstract class's run() and return immediately on a public holiday, otherwise run as before.

lafual
  • 683
  • 1
  • 7
  • 23
  • 1
    The documentation says it all `Wraps a CronExpression`. It is used internally. If you want to do something else, write your own trigger implementation. – M. Deinum Apr 11 '23 at 05:59
  • It seems like I need to use this https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/SchedulingConfigurer.html . Example usage here; https://stackoverflow.com/questions/68711988/schedulingconfigurer-crontrigger-regex-is-not-working-as-expected – lafual Apr 11 '23 at 06:28
  • No you don't. As that is for onetime scheduling, without a proper trigger implementation it won't work either. – M. Deinum Apr 11 '23 at 06:32

1 Answers1

0

A crude example based on seconds (not holidays) which has a schedule of every 5 seconds, but skips 10, 20 etc (inspired this post SchedulingConfigurer/CronTrigger regex is not working as expected)

package com.example.demo;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.scheduling.support.CronTrigger;

@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {

    @Bean(destroyMethod = "shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(100);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
        
        taskRegistrar.addTriggerTask(new Runnable() {
            @Override
            public void run() {
                System.out.println("Running: " + LocalDateTime.now());
            }
        }, new Trigger() {
            
            static final String CRON_EXP = "0/5 * * * * *";
            
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                System.err.println("Next Execution Time >>>");
                
                CronTrigger trigger = new CronTrigger(CRON_EXP);
                Date nextRegular = trigger.nextExecutionTime(triggerContext);
                
                if (isHoliday(nextRegular)) {
                    CronExpression cronExpression = CronExpression.parse(CRON_EXP);
                    LocalDateTime next = cronExpression.next(LocalDateTime.now().plusSeconds(5)); // should be 00:00:01 of next non-holiday.
                    nextRegular = java.sql.Timestamp.valueOf(next);
                }
                return nextRegular;
            }
            
            private boolean isHoliday(Date d) {
                LocalDateTime ldt = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                return ldt.getSecond() % 10 == 0; // should do database look-up here.
            }
        });
    }
}

Date nextExecutionTime() is deprecated in Spring Framework 6 in favour of Instant nextExecution(), so this answer is for Spring Framework 5.

lafual
  • 683
  • 1
  • 7
  • 23