How to use @Configuration and @EnableScheduling together with Spring Batch
Asked Answered
M

1

7

Since "Only void-returning methods may be annotated with @Scheduled", how can I use Spring Batch and Spring Scheduler Task when I am using @Bean configuration instead of xml configuration? Below you can find my complete configuration file. It is running perfectly when I trigger from main() but only once. I want to add @Scheduled(fixedrate=9999) in order to evoke same job at certain frequency. As far as I can see, in order to do this, I was expected to add @Scheduled around step1 method but I can't as it returns different from void.

@Configuration
@EnableBatchProcessing
@EnableScheduling
public class BatchConfiguration {
       private static final Logger log = LoggerFactory
                     .getLogger(BatchConfiguration.class);

       @Bean
       @StepScope
       public FlatFileItemReader<Person> reader() {
              log.info(new Date().toString());
              FlatFileItemReader<Person> reader = new FlatFileItemReader<Person>();
              reader.setResource(new ClassPathResource("test_person_json.js"));
              reader.setLineMapper(new DefaultLineMapper<Person>() {
                     {
                           setLineTokenizer(new DelimitedLineTokenizer() {
                                  {
                                         setNames(new String[] {"firstName", "lastName" });
                                  }
                           });
                           setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {
                                  {
                                         setTargetType(Person.class);
                                  }
                           });
                     }
              });
              return reader;
       }

       @Bean
       public ItemProcessor<Person, Person> processor() {
              return new PersonItemProcessor();
       }

       @Bean
       public ItemWriter<Person> writer(DataSource dataSource) {
              JdbcBatchItemWriter<Person> writer = new JdbcBatchItemWriter<Person>();
              writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Person>());
              writer.setSql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)");
              writer.setDataSource(dataSource);
              return writer;
       }

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

       @Bean
       public Step step1(StepBuilderFactory stepBuilderFactory,
                     ItemReader<Person> reader, ItemWriter<Person> writer,
                     ItemProcessor<Person, Person> processor) {
              return stepBuilderFactory.get("step1").<Person, Person> chunk(10)
.reader(reader).processor(processor).writer(writer).build();
       }

       @Bean
       public JdbcTemplate jdbcTemplate(DataSource dataSource) {
              return new JdbcTemplate(dataSource);
       }
}


//Question updated on Dec 3th 2015 with first suggestion
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class PersonScheduler {
    private Job myImportJob;
    private JobLauncher jobLauncher;

    @Autowired
    public PersonScheduler(JobLauncher jobLauncher, @Qualifier("myImportJob") Job myImportJob){
        this.myImportJob = myImportJob;
        this.jobLauncher = jobLauncher;
   }

   @Scheduled(fixedRate=9999)
   public void runJob{
       jobLauncher.run(myImportJob, new JobParameters());
   }
}
Maunder answered 1/12, 2015 at 21:17 Comment(0)
S
4

Just create separate component, where you autowire your job and schedule it:

@Component
public class MyScheduler{
    private Job myImportJob;
    private JobLauncher jobLauncher;

    @Autowired
    public MyScheduler(JobLauncher jobLauncher, @Qualifier("myImportJob") Job myImportJob){
        this.myImportJob = myImoportJob; 
        this.jobLauncher = jobLauncher;
   }

   @Scheduled(fixedRate=9999)
   public void runJob(){
       jobLauncher.run(myImportJob, new JobParameters());
   }
}

Reaction on third comment:

Just use .allowStartIfComplete(true) when you are creating the step.

Swinney answered 2/12, 2015 at 10:22 Comment(4)
I am getting two warnings: 1 - (right on @Scheduled)The annotation @Scheduled is disallowed for this location 2 - (right on method runJob)Multiple markers at this line - Syntax error, insert ";" to complete FieldDeclaration - void is an invalid type for the variable runJob I updated my question adding your suggestion. I showed the imports as well as usually such warnings appear when we don't have the correct imports but I guess I am correct on it. Should I add @SuppressWarnings?Maunder
I forgit brackets for runJob() method. It's updated now.Swinney
I can see the scheduller is working but I am getting SimpleStepHandler : Step already complete or not restartable, so no action to execute: StepExecution: id=1, version=3, name=step1, status=COMPLETED, exitStatus=COMPLETED, readCount=1, filterCount=0, writeCount=1 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=. It seems to me that after my job ran twice, it can't be ran anymore. I guess, the first time is running as I start the application, then the second as the scheduller triggers. After this, the status is "completed" forever.Maunder
Thanks, you have helped me considerably.Maunder

© 2022 - 2024 — McMap. All rights reserved.