Schedule a task with Cron which allows dynamic update
Asked Answered
F

2

13

I use sprint boot 1.3, spring 4.2

In this class

@Service
public class PaymentServiceImpl implements PaymentService {
    ....
    @Transactional
    @Override
    public void processPayment() {
        List<Payment> payments = paymentRepository.findDuePayment();
        processCreditCardPayment(payments);
    }
}

I would like to call processPayment every x moment.

This x moment is set in a database. The user can modify it.

So i think i can't use anotation.

I started to this this

@EntityScan(basePackageClasses = {MyApp.class,     Jsr310JpaConverters.class})
@SpringBootApplication
@EnableCaching
@EnableScheduling
public class MyApp {

    @Autowired
    private DefaultConfigService defaultConfigService;

    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }

    @Bean
    public TaskScheduler poolScheduler() {
        SimpleAsyncTaskExecutor taskScheduler = new SimpleAsyncTaskExecutor();

        DefaultConfigDto defaultConfigDto = defaultConfigService.getByFieldName("payment-cron-task");
        String cronTabExpression = "0 0 4 * * ?";
        if (defaultConfigDto != null && !defaultConfigDto.getFieldValue().isEmpty()) {
            cronTabExpression = "0 0 4 * * ?";
        }

        appContext.getBean("scheduler");

        taskScheduler.schedule(task, new CronTrigger(cronTabExpression));
        return scheduler;
    }

Maybe it's not the good way.

Any suggestion?

Don't know if to get my context if i need to create a property like

@Autowired
ConfigurableApplicationContext context;

and after in the main

public static void main(String[] args) {
        context = SpringApplication.run(MyApp.class, args);
}
Footlights answered 19/5, 2016 at 22:56 Comment(0)
H
21

Looking at the question seems like you want to update the scheduler, without restart.

The code you have shared only ensures the config is picked from DB, but it will not refresh without application restart.

The following code will use the default scheduler available in the spring context and dynamically compute the next execution time based on the available cron setting in the DB:

Here is the sample code:

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.CronTrigger;

@SpringBootApplication
@EnableScheduling
public class Perses implements SchedulingConfigurer {
    private static final Logger log = LoggerFactory.getLogger(Perses.class);
   
    @Autowired
    private DefaultConfigService defaultConfigService;

    @Autowired
    private PaymentService paymentService;

    public static void main(String[] args) {
        SpringApplication.run(Perses.class, args);
    }

    private String cronConfig() {
        String cronTabExpression = "*/5 * * * * *";
        if (defaultConfigDto != null && !defaultConfigDto.getFieldValue().isEmpty()) {
            cronTabExpression = "0 0 4 * * ?";
        }
        return cronTabExpression;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(new Runnable() {
            @Override
            public void run() {
                paymentService.processPayment();
            }
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                String cron = cronConfig();
                log.info(cron);
                CronTrigger trigger = new CronTrigger(cron);
                Date nextExec = trigger.nextExecutionTime(triggerContext);
                return nextExec;
            }
        });
    }
}
Haphtarah answered 20/5, 2016 at 0:5 Comment(5)
i search to be able to reflect the change dinamically without restarting the application.Footlights
Updated the java code to have dynamic cron. Hope you put enough guards on the db entry, so that nobody messes it up.Haphtarah
Liked the mechanism. Do we have any way to update the cron when the source (external properties file, db changes etc) changes?Coatee
I thought the point of this question was if it was possible to update the cron tasks dynamically, not just read it from a database once. Can you provide any info on this?Toile
Hello , I have some issue like @Simon , I want to update my dynamically jobs without restarting the whole application !Bathurst
B
2

Just if someone still having this issue a better solution getting value from database whenever you want without many changes would be run cron every minute and get mod between current minute versus a configurated value delta from database, if this mod is equals to 0 means it has to run like if it is a mathematical multiple, so if you want it to run every 5 minutes for example delta should be 5.

A sample:

@Scheduled(cron = "0 */1 * * * *") //fire every minute
public void perform() {

    //running
    Integer delta = 5;//get this value from databse
    Integer minutes = getField(Calendar.MINUTE)//calendar for java 7;
    Boolean toRun = true;//you can also get this one from database to make it active or disabled
  
    toRun = toRun && (minutes % delta == 0);
    if (toRun && (!isRunning)) {
       isRunning = true;
       try {
         //do your logic here
       } catch (Exception e) { }
        isRunning = false;
    }
}

public Integer getField(int field) {

    Calendar now = Calendar.getInstance();

    if(field == Calendar.MONTH) {
        return now.get(field)+ 1; // Note: zero based!
    }else {
        return now.get(field);
    }
}

Hope this help :D

Borecole answered 25/1, 2021 at 15:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.