Hot questions for Spring Scheduling Tasks

Top 10 Java Open Source / Spring / Spring Scheduling Tasks

Question:

I am writing an application that has a cron job that executes every 60 seconds. The application is configured to scale when required onto multiple instances. I only want to execute the task on 1 instance every 60 seconds (On any node). Out of the box I can not find a solution to this and I am surprised it has not been asked multiple times before. I am using Spring 4.1.6.

    <task:scheduled-tasks>
        <task:scheduled ref="beanName" method="execute" cron="0/60 * * * * *"/>
    </task:scheduled-tasks>

Answer:

There is a ShedLock project that serves exactly this purpose. You just annotate tasks which should be locked when executed

@Scheduled( ... )
@SchedulerLock(name = "scheduledTaskName")
public void scheduledTask() {
   // do something
}

Configure Spring and a LockProvider

@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "30s")
class MySpringConfiguration {
    ...
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
       return new JdbcTemplateLockProvider(dataSource);
    }
    ...
}

Question:

@Scheduled(fixedDelay = 5000)
public void myJob() {
     Thread.sleep(12000);
}

How can I prevent this spring job from running if the previous routine is not yet finished?


Answer:

by default, spring uses a single-threaded Executor. so no two @Scheduled tasks will ever overlap. even two @Scheduled methods in completely unrelated classes will not overlap simply because there is only a single thread to execute all @Scheduled tasks.

furthermore, even if you replace the default Executor with a thread pool based executor, those Executors will typically delay the execution of a task instance until the previously scheduled instance completes. this is true for fixedDelay, fixedInterval, and cron based schedules. for example, this spring configuration will create a ScheduledThreadPoolExecutor that uses a threadpool, but does not allow concurrent instances of the same schedule just as you desire:

@Configuration
@EnableScheduling
...
public class MySpringJavaConfig {
    @Bean(destroyMethod = "shutdown")
    public Executor taskScheduler() {
        return Executors.newScheduledThreadPool(5);
    }
    ...
}

here is the javadoc for ScheduledThreadPoolExecutor::scheduleAtFixedRate which specifies:

If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.

note: this functionality does not hold true for @Async tasks. spring will create as many concurrent instances of those as needed (if there are sufficient threads in the pool).

Question:

I've been thinking around the Java feature that evaluates annotation values in compile-time and it seems to really make difficult externalizing annotation values.

However, I am unsure whether it is actually impossible, so I'd appreciate any suggestions or definitive answers on this.

More to the point, I am trying to externalize an annotation value which controls delays between scheduled method calls in Spring, e.g.:

public class SomeClass {

    private Properties props;
    private static final long delay = 0;

    @PostConstruct
    public void initializeBean() {
        Resource resource = new ClassPathResource("scheduling.properties");
        props = PropertiesLoaderUtils.loadProperties(resource);
        delay = props.getProperties("delayValue");
    }

    @Scheduled(fixedDelay = delay)
    public void someMethod(){
        // perform something
    }
}

Suppose that scheduling.properties is on classpath and contains property key delayValue along with its corresponding long value.

Now, this code has obvious compilation errors since we're trying to assign a value to final variable, but that is mandatory, since we can't assign the variable to annotation value, unless it is static final.

Is there any way of getting around this? I've been thinking about Spring's custom annotations, but the root issue remains - how to assign the externalized value to annotation?

Any idea is welcome.

EDIT: A small update - Quartz integration is overkill for this example. We just need a periodic execution with sub-minute resolution and that's all.


Answer:

The @Scheduled annotation in Spring v3.2.2 has added String parameters to the original 3 long parameters to handle this. fixedDelayString, fixedRateString and initialDelayString are now available too:

 @Scheduled(fixedDelayString = "${my.delay.property}")
 public void someMethod(){
        // perform something
 }

Question:

I am new to Spring-boot(version 1.3.6) and Quartz and I am wondering what is the difference between making a task with Spring-scheduler:

    @Scheduled(fixedRate = 40000)
    public void reportCurrentTime() {
        System.out.println("Hello World");
    }

And the Quartz way:

0. Create sheduler.
1. Job which implements Job interface.
2. Create JobDetail which is instance of the job using the builder  org.quartz.JobBuilder.newJob(MyJob.class)
3. Create a Triger
4. Finally set the job and the trigger to the scheduler

In code:

  public class HelloJob implements Job {

    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.err.println("Hello!");
    }
  }

and the sheduler:

SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

  Scheduler sched = schedFact.getScheduler();

  sched.start();

  // define the job and tie it to our HelloJob class
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1")
      .build();

  // Trigger the job to run now, and then every 40 seconds
  Trigger trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()
          .withIntervalInSeconds(40)
          .repeatForever())
      .build();

  // Tell quartz to schedule the job using our trigger
  sched.scheduleJob(job, trigger);

Does Quartz provide more flexible way to define Jobs, Triggers and Schedulers or Spring Scheduler has something else which is better?


Answer:

Spring Scheduler is an abstraction layer written to hide the implementations of Executors in different JDKs like Java SE 1.4, Java SE 5 and Java EE environments, which have their own specific implementations.

Quartz Scheduler is a fully fledged scheduling framework which allows CRON based or Simple periodic task execution.

Spring Scheduler does provide integration with Quartz scheduler in the form of a Trigger to use the full functionality of the Quartz scheduler.

Advantage of using Spring Scheduler without directly using the Quartz Scheduler specific classes is that the abstraction layer provides flexibility and loose coupling.

Question:

How to customize the exception handling for @Scheduled annotation from spring ?

I have Cron jobs which will be triggered in the server (Tomcat 6) and when any exceptions occur I need to do some handling.

  • Spring version 3.2
  • Tomcat Server 6

Answer:

If you want to use Java Config you will need to create configuration implementing SchedulingConfigurer

@EnableScheduling
@Configuration
class SchedulingConfiguration implements SchedulingConfigurer {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final ThreadPoolTaskScheduler taskScheduler;

    SchedulingConfiguration() {
        taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setErrorHandler(t -> logger.error("Exception in @Scheduled task. ", t));
        taskScheduler.setThreadNamePrefix("@scheduled-");

        taskScheduler.initialize();
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskScheduler);
    }
}

You can modify error handler for your needs. Here I only log a message.

Don't forget to call taskScheduler.initialize();. Without it you'll get:

java.lang.IllegalStateException: ThreadPoolTaskScheduler not initialized

Question:

I am using the @Scheduled annotation from Spring framework to invoke a method. But I have multiple nodes in my setup and I do not want them to all run at exactly the same time. So I'd like to set a random value to the initial delay to offset them from each other.

import org.springframework.scheduling.annotation.Scheduled;

@Scheduled(fixedRate = 600000, initialDelay = <random number between 0 and 10 minutes> )

Unfortunately, I am only allowed to use a constant expression here. Is there any other way around this? I thought of using Spring expression language.


Answer:

To make the initial delay randomly somewhere between 0 and the fixedRate try this:

@Scheduled(fixedDelayString = "${some.delay}", initialDelayString = "${random.int(${some.delay})}")

Where you define some.delay (but pick a more suitable name) as 10 minutes as a property like so in your application.properties or equivalent.

some.delay = 600000

Of course if you want to be lazy and hard code it you can always just use ${random.int(600000)}

Question:

I have used Spring Framework's Scheduled to schedule my job to run at every 5 mins using cron. But sometime my job waits infinitely for an external resource and I can't put timeout there. I can't use fixedDelay as previous process sometime goes in wait infinitely mode and I have to refresh data at every 5 mins.

So I was looking any option in Spring Framework's Scheduled to stop that process/thread after a fixed-time either it run successfully or not.

I have found below setting which initialized ThreadPoolExecutor with 120 seconds for keepAliveTime which I put in @Configuration class. Can anybody tell me will this work as I expected.

@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
    int coreThreads = 8;
    int maxThreads = 20;
    final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            coreThreads, maxThreads, 120L, 
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()
    );
    threadPoolExecutor.allowCoreThreadTimeOut(true);

    return threadPoolExecutor;
}

Answer:

I'm not sure this will work as expected. Indeed the keepAlive is for IDLE thread and I don't know if your thread waiting for resources is in IDLE. Furthermore it's only when the number of threads is greater than the core so you can't really know when it happen unless you monitor the threadpool.

keepAliveTime - when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.

What you can do is the following:

public class MyTask {

    private final long timeout;

    public MyTask(long timeout) {
        this.timeout = timeout;
    }

    @Scheduled(cron = "")
    public void cronTask() {
        Future<Object> result = doSomething();
        result.get(timeout, TimeUnit.MILLISECONDS);
    }

    @Async
    Future<Object> doSomething() {
        //what i should do
        //get ressources etc...
    }
}

Don't forget to add @EnableAsync

It's also possible to do the same without @Async by implementing a Callable.

Edit: Keep in mind that it will wait until timeout but the thread running the task won't be interrupted. You will need to call Future.cancel when TimeoutException occurs. And in the task check for isInterrupted() to stop the processing. If you are calling an api be sure that isInterrupted() is checked.

Question:

I am using spring scheduler.This works fine but when I changed cron.expression value in application.properties every time I need to restart tomcat server.Is there any way that I can make it dynamic like automatically changes will reflect ? I did google also but didnt got any solution for my app.I have given code snippet as follows:

application.properties

cron.expression=0 58 23 * * ?

@Scheduled(cron = "${cron.expression}", zone = "IST")
public void sendEmail() throws Exception {



}

Answer:

It's an easy task if you are working on spring-cloud. Spring has a project called 'spring cloud config' which helps you to achieve the externalize the properties and whenever there is a change, that change will push to your service. Have a look at setting up spring cloud config

You may find many samples for same. Let me know if you are looking for code.

Question:

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);
}

Answer:

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;
            }
        });
    }
}

Question:

Here is the Scheduling Configuration

@Configuration
@EnableScheduling
public class RWInventorySchedule {

protected org.slf4j.Logger log = LoggerFactory.getLogger(RWInventorySchedule.class);

@PersistenceContext
private EntityManager entityManager;


   @Bean
   public RWInventoryProcessor constructInventoryProcessor() {
       log.debug("RWInventorySchedule constructs InventoryProcessor, entityManager : {} " , entityManager);
       return new RWInventoryProcessor(entityManager);
    }
}

Inventory Processor is the following

public class RWInventoryProcessor  {
 ...
 @Scheduled(fixedRate = 5000,initialDelay = 3000)
 @Transactional
 public void runProcess() {
   ...
 }
}

During execution, getting the following errors in debug log

DEBUG org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor - Could not find default TaskScheduler bean org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.scheduling.TaskScheduler' available ... DEBUG org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor - Could not find default ScheduledExecutorService bean org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.concurrent.ScheduledExecutorService' available

Am I missing anything


Answer:

If you're using Java configuration you need an @Bean definition for the type of scheduler you wish to use. Spring does not have a default bean for this. For example

@Bean
public TaskScheduler taskScheduler() {
    return new ConcurrentTaskScheduler(); //single threaded by default
}