Problem
Suppose you have three steps (first step 1 then 2 and finally 3) in a Spring Batch java project. So imagine that you want to start with Step 1, then do some calculations and then decide if you want to skip step 2 or continue normally.
Something like this:
Solution
What you need is to add a tool called decisor that will run between steps 1 and 2. Let us see how.
The first thing we should do is to create the class that will take the decision. This class will implement the following interface:
\src\main\java\com\hjbello\JobExecutionDecider.java
This is the class that we will need:
\src\main\java\com\hjbello\FlowDecision.java
This is a dummy ejemple, what it will do is to obtain a random integer number randomInt;
To use this class as a "step" in between 1 and 2 we will have to define our job in the following way. This is the class in which we configure the job
\src\main\java\com\hjbello\BatchConfiguration.java
If we want to do this with xml style instead of full-java configuration we would have to use a module-context.xml file like this one:
The first thing we should do is to create the class that will take the decision. This class will implement the following interface:
\src\main\java\com\hjbello\JobExecutionDecider.java
package com.hjbello; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.job.flow.FlowExecutionStatus; public interface JobExecutionDecider { FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution); }
This is the class that we will need:
\src\main\java\com\hjbello\FlowDecision.java
package com.hjbello; import java.util.Random; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.job.flow.FlowExecutionStatus; import org.springframework.batch.core.job.flow.JobExecutionDecider; import org.springframework.stereotype.Component; @Component("decider") public class FlowDecision implements JobExecutionDecider { private static final Log log = LogFactory.getLog(NoOpItemReaderStep1.class);
public static final String COMPLETED = "COMPLETED"; public static final String FAILED = "FAILED"; public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) { Random generator = new Random(); int randomInt = generator.nextInt(); log.info("Executing Decision with randomInt = " + randomInt); if(randomInt % 2 == 0){ log.info("------------------------------------------"); log.info("Completed -> go to step 2"); log.info("------------------------------------------"); return FlowExecutionStatus.COMPLETED; } log.info("------------------------------------------"); log.info("Failed -> go to step 3"); log.info("------------------------------------------"); return FlowExecutionStatus.FAILED; } }
This is a dummy ejemple, what it will do is to obtain a random integer number randomInt;
- if randomInt is even it will go to step 2
- otherwise it will skip step 2 and go to step 3.
To use this class as a "step" in between 1 and 2 we will have to define our job in the following way. This is the class in which we configure the job
\src\main\java\com\hjbello\BatchConfiguration.java
package com.hjbello; import javax.batch.api.Decider; import javax.sql.DataSource; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.job.builder.FlowBuilder; import org.springframework.batch.core.job.flow.Flow; import org.springframework.batch.core.job.flow.FlowExecutionStatus; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableBatchProcessing public class BatchConfiguration { @Autowired public JobBuilderFactory jobBuilderFactory; @Autowired public StepBuilderFactory stepBuilderFactory; @Autowired public DataSource dataSource; @Bean public Job importJob(JobBuilderFactory jobs) { FlowBuilder<Flow> flowBuilder = new FlowBuilder<Flow>("flow1"); Flow flow = flowBuilder .start(step1()) .next(decision()) .on(decision().COMPLETED) .to(step2()) .from(decision()) .on(decision().FAILED) .to(step3()) .end(); return jobs.get("importUserLoopJob") .incrementer(new RunIdIncrementer()) .start(flow) .end() .build(); } // STEPS ----------------------------------------- public Step step1() { return stepBuilderFactory.get("step1") .<String, String> chunk(10) .reader(readerStep1()) .writer(writerStep1()) .build(); } @Bean public Step step2() { return stepBuilderFactory.get("step2") .<String, String> chunk(10) .reader(readerStep2()) .writer(writerStep2()) .build(); } @Bean public Step step3() { return stepBuilderFactory.get("step3") .tasklet(tasklet()) .build(); } // TASKLETS READERS AND WRITERS ------------------- @Bean public NoOpItemReaderStep2 readerStep2(){ return new NoOpItemReaderStep2(); } @Bean public NoOpItemReaderStep1 readerStep1(){ return new NoOpItemReaderStep1(); } @Bean public NoOpItemWriterStep2 writerStep2(){ return new NoOpItemWriterStep2(); } @Bean public NoOpItemWriterStep1 writerStep1(){ return new NoOpItemWriterStep1(); } @Bean public TaskletStep3 tasklet(){ return new TaskletStep3(); } @Bean public FlowDecision decision(){ return new FlowDecision(); } }
If we want to do this with xml style instead of full-java configuration we would have to use a module-context.xml file like this one:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch" xsi:schemaLocation=" http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <description>Example job to get you started. It provides a skeleton for a typical batch application.</description> <batch:job id="job1"> <batch:step id="step1" next="decision"> <batch:tasklet transaction-manager="transactionManager" start-limit="100"> <batch:chunk reader="readerStep1" writer="writerStep1" commit-interval="1" /> </batch:tasklet> </batch:step> <batch:decision id="decision" decider="decider"> <batch:next on="FAILED" to="step3" /> <batch:next on="COMPLETED" to="step2" /> </batch:decision> <batch:step id="step2" next="step3"> <batch:tasklet transaction-manager="transactionManager" start-limit="100"> <batch:chunk reader="readerStep2" writer="writerStep2" commit-interval="1" /> </batch:tasklet> </batch:step> <batch:step id="step3"> <batch:tasklet ref="taskletStep3" transaction-manager="transactionManager" start-limit="100"> </batch:tasklet> </batch:step> </batch:job> </beans>
Lets see how it runs:
- It starts with step 1:
- Since the number is even commands the flow to go to step 2
- Starts step 2
- Ends with step 3.
Great. I have different scenario if the random value is even it will execute step 2 and step 3. The value is odd it will skip step 2 and execute step 3
ReplyDelete