How to dynamically schedule a Spring Batch job with ThreadPoolTaskScheduler

10,556

Solution 1

I finally got how to do it !

I created a class which implements Runnable (and for convenience, extends Thread, which avoid the need to implement all of Runnable classes).

@Component
public class MyRunnableJob extends Thread implements Runnable{

    private Job job;
    private JobParameters jobParameters;
    private final JobOperator jobOperator;

    @Autowired
    public MyRunnableJob(JobOperator jobOperator) {
        this.jobOperator = jobOperator;
    }

    public void setJob(Job job){
        this.job=job;
    }

    @Override
    public void run(){
        try {
            String dateParam = new Date().toString();
            this.jobParameters = new JobParametersBuilder().addString("date", dateParam).toJobParameters();
            System.out.println("jobName : "+job.getName()+" at "+dateParam);

            jobOperator.start(job.getName(), jobParameters.toString());
        } catch (NoSuchJobException | JobInstanceAlreadyExistsException | JobParametersInvalidException ex) {
            Logger.getLogger(MyRunnableJob.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

In my ScheduledProcessor class, I set a Job to myRunnable class and then pass it as a parameter of the schedule method.

public class SchedulingProcessor {

    //Autowired fields :
    private final JobLauncher jobLauncher;
    private final Job importUserJob;
    private final ThreadPoolTaskScheduler  threadPoolTaskScheduler;
    private final MyRunnableJob myRunnableJob;


    //Other fields :
    private List<ScheduledFuture> scheduledTasks;


    @Autowired
    public SchedulingProcessor(JobLauncher jobLauncher, Job importUserJob, ThreadPoolTaskScheduler  threadPoolTaskScheduler, MyRunnableJob myRunnableJob) throws Exception {
        this.jobLauncher=jobLauncher;
        this.importUserJob=importUserJob;
        this.threadPoolTaskScheduler=threadPoolTaskScheduler;
        this.myRunnableJob=myRunnableJob;
        Trigger trigger = new CronTrigger("0/6 * * * * *");
        myRunnableJob.setJob(this.importUserJob);
        scheduledTasks = new ArrayList();
        scheduledTasks.add(this.threadPoolTaskScheduler.schedule((Runnable) myRunnableJob, trigger));
    }        
}

The scheduledTasks list is just to keep a control over the tasks I just scheduled.

This trick enabled me to dynamically (thanks to ThreadPoolTaskScheduler) schedule Spring Batch Jobs encapsulated in a class implementing Runnable. I wish it can help someone in the same case as mine.

Solution 2

Heres another way to trigger them from your spring context.

    Job emailJob = (Job) applicationContext.getBean("xyzJob");
    JobLauncher launcher = (JobLauncher) applicationContext
            .getBean("jobLauncher");
    launcher.run(emailJob, new JobParameters());
Share:
10,556
midemarc
Author by

midemarc

I am a IT student, specialized in software development in the Exia Cesi school, France. I really enjoy learning by myself, either researching online tutorials or through trial and errors. I often end up being stuck, in which cases StackOverflow often has answers already posted about my problems. When I can't find any and keep troubleshooting without success, I do my best to ask good questions here.

Updated on June 04, 2022

Comments

  • midemarc
    midemarc about 2 years

    I have a Spring Batch application in which I want to schedule jobs calls.

    The scheduling interval is not known at build so I can't just annotate my Job with @Scheduled.This led me to use a ThreadPoolTaskScheduler.

    The thing is the method schedule takes a Runnable as a parameter. Is it possible to schedule jobs this way ? I can call the job directly from the following service but I can't schedule it.

    Here is my the background of my problem, I tried to make it simple :

    @Service
    public class ScheduledProcessor{
        private final ThreadPoolTaskScheduler  threadPoolTaskScheduler;
        private Application application;   
        @Autowired
        public ScheduledProcessor(ThreadPoolTaskScheduler threadPoolTaskScheduler, Application application){
            this.threadPoolTaskScheduler = threadPoolTaskScheduler;
            this.application = application;
            scheduledTasks = new ArrayList();
    
    
            Trigger trigger = new CronTrigger("0/6 * * * * *");
    //Here I am trying to schedule my job.
    //The following line is wrong because a Job can't be cast to a Runnable but I wanted to show the intended behaviour.            
            threadPoolTaskScheduler.schedule((Runnable) application.importUserjob, trigger);
            System.out.println("Job launch !");
        }
    

    And here is the JobBuilderFactory :

    @Bean
    public Job importUserJob(JobBuilderFactory jobs, Step s1, Step s2) {
        return jobs.get("importUserJob")
                .incrementer(new RunIdIncrementer())
                .flow(s1)
                .end()
                .build();
    }
    

    I understand (well, I'm even not sure about that) that I can't directly cast a Job to a Runnable but is it possible to convert it in any way ? Or can you give me some advice about what to use for being able to dynamically schedule spring batch jobs ?

    In case that changes something, I also need to be able to restart / skip my steps, as I currently can with the threadPoolTaskScheduler.

    Thank you in advance for any help or hint you could provide.

  • Marvin Richter
    Marvin Richter about 7 years
    Just as an information, if you extend Thread you don't need to implement Runnable explicitly because Thread already implements it.
  • smoczyna
    smoczyna almost 7 years
    I came accross htis post when looking for dynamically scheduled jobs and got a question. How is that suppose to be dynamic actually? Could you elaborate how are you scheduling any job in runtime with this?
  • smoczyna
    smoczyna almost 7 years
    From other hand this code fails when more than 1 qualifying job exists in the app, it needs @Qualifier("job name") in the constuctor to work well and this disqualifies other jobs to be scheduled with this.
  • midemarc
    midemarc almost 7 years
    @MarvinRichter : I think I had a reason to do so back then, but have no clue about it now… It may have been that I didn't understand the whole thing properly. Very likely actually.
  • midemarc
    midemarc almost 7 years
    @smoczyna Sure. When you want to schedule a job in Spring Batch, you annotate it with @Scheduled(cron = "your-cron-expression"). This is "static" since you can't put a variable for the cron expression : it is assigned before runtime. Here, you can put a variable in CronTrigger. So it is "dynamic" in the sense that the value can change while the code runs. Once it is set though, changing the value in the variable won't update it but there are probably other ways for that.