728x90
1. HelloWorld 띄우기
2-1. Job Parameter ChunkContext
2-2. Job Parameter 늦은 바인딩
3-1. Job Parameter 유효성 검증하기 1
3-2. Job Parameter 유효성 검증하기 2
4-1. Job Parameter 증가시키기
4-2. Job Parameter 자동 타임스탬프 사용하기
5-1. Job Listener - JobExecutionListener 구현
5-2. Job Listener - 애너테이션 사용하기
6-1. Job ExecutionContext 조작하기 - Job 의 ExecutionContext 에 foo 데이터 추가하기
6-2. Job ExecutionContext 조작하기 - Step 의 ExecutionContext 에 foo 데이터 추가하기
6-3. Job ExecutionContext 에 foo 키 승격하기
7-1 Step-Flow
1. HelloWorld 띄우기
/**
* Hello World Job
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch01 {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet((contribution, chunkContext) -> {
System.out.println("contribution = " + contribution);
System.out.println("chunkContext = " + chunkContext);
System.out.println("Hello World");
return RepeatStatus.FINISHED;
})
.build();
}
}
2-1. Job Parameter ChunkContext
/**
* Job Parameter ChunkContext
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch02_1 {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet())
.build();
}
private Tasklet helloWorldTasklet() {
return (contribution, chunkContext) -> {
String foo = (String) chunkContext.getStepContext()
.getJobParameters()
.get("foo");
System.out.println("foo = " + foo);
return RepeatStatus.FINISHED;
};
}
}
2-2. Job Parameter 늦은 바인딩
/**
* Job Parameter 늦은 바인딩
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch02_2 {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null))
.build();
}
/**
* 실행 명령어
* // 스프링 배치의 JobParameters 는 스프링 부트의 명령행 기능을 사용해 프로퍼티를 구성하는 것과 다르다
* // 따라서 -- 접두사 또는 -D 아규먼트를 사용하면 안된다.
* java -jar ./build/libs/demo-0.0.1-SNAPSHOT.jar foo=bar
* java -jar ./build/libs/demo-0.0.1-SNAPSHOT.jar -foo=bar
* // - 를 붙이면 특정 잡파라미터는 식별에는 사용되지 않도록 함(중복실행가능하다는 뜻)
* @param foo
* @return
*/
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}") String foo) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
return RepeatStatus.FINISHED;
};
}
}
3-1. Job Parameter 유효성 검증하기 1
/**
* Job Parameter 유효성 검증하기 한개
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch03_1 {
public static class ParameterValidator implements JobParametersValidator{
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
System.out.println("ParameterValidator.validate");
System.out.println("parameters = " + parameters);
System.out.println("parameters foo = " + parameters.getString("foo"));
String foo = parameters.getString("foo");
if (!StringUtils.hasText(foo))
throw new JobParametersInvalidException("foo must not empty");
else if (!StringUtils.endsWithIgnoreCase(foo, "bar"))
throw new JobParametersInvalidException("foo must is bar");
}
}
@Bean
public JobParametersValidator validator() {
DefaultJobParametersValidator validator = new DefaultJobParametersValidator();
validator.setRequiredKeys(new String[]{"foo"});
validator.setOptionalKeys(new String[]{"name"});
return validator;
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.validator(validator())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null, null))
.build();
}
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}")String foo,
@Value("#{jobParameters['name']}")String name) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
System.out.println("name = " + name);
return RepeatStatus.FINISHED;
};
}
}
3-2. Job Parameter 유효성 검증하기 2
/**
* Job Parameter 유효성 검증하기 두개
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch03_2 {
public static class ParameterValidator implements JobParametersValidator{
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
System.out.println("ParameterValidator.validate");
System.out.println("parameters = " + parameters);
System.out.println("parameters foo = " + parameters.getString("foo"));
String foo = parameters.getString("foo");
if (!StringUtils.hasText(foo))
throw new JobParametersInvalidException("foo must not empty");
else if (!StringUtils.endsWithIgnoreCase(foo, "bar"))
throw new JobParametersInvalidException("foo must is bar");
}
}
@Bean
public CompositeJobParametersValidator validator() {
CompositeJobParametersValidator validator = new CompositeJobParametersValidator();
DefaultJobParametersValidator defaultJobParametersValidator = new DefaultJobParametersValidator();
defaultJobParametersValidator.setRequiredKeys(new String[]{"foo"});
defaultJobParametersValidator.setOptionalKeys(new String[]{"name"});
defaultJobParametersValidator.afterPropertiesSet();
validator.setValidators(
Arrays.asList(new ParameterValidator(), defaultJobParametersValidator)
);
return validator;
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.validator(validator())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null, null))
.build();
}
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}")String foo,
@Value("#{jobParameters['name']}")String name) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
System.out.println("name = " + name);
return RepeatStatus.FINISHED;
};
}
}
4-1. Job Parameter 증가시키기
/**
* Job Parameter 증가시키기
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch04_1 {
public static class ParameterValidator implements JobParametersValidator{
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
System.out.println("ParameterValidator.validate");
System.out.println("parameters = " + parameters);
System.out.println("parameters foo = " + parameters.getString("foo"));
String foo = parameters.getString("foo");
if (!StringUtils.hasText(foo))
throw new JobParametersInvalidException("foo must not empty");
else if (!StringUtils.endsWithIgnoreCase(foo, "bar"))
throw new JobParametersInvalidException("foo must is bar");
}
}
@Bean
public CompositeJobParametersValidator validator() {
CompositeJobParametersValidator validator = new CompositeJobParametersValidator();
DefaultJobParametersValidator defaultJobParametersValidator = new DefaultJobParametersValidator();
defaultJobParametersValidator.setRequiredKeys(new String[]{"foo"});
defaultJobParametersValidator.setOptionalKeys(new String[]{"name", "run.id"});
defaultJobParametersValidator.afterPropertiesSet();
validator.setValidators(
Arrays.asList(new ParameterValidator(), defaultJobParametersValidator)
);
return validator;
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.validator(validator())
.incrementer(new RunIdIncrementer())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null, null, null))
.build();
}
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}")String foo,
@Value("#{jobParameters['name']}")String name,
@Value("#{jobParameters['run.id']}")Long id) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
System.out.println("name = " + name);
System.out.println("id = " + id);
return RepeatStatus.FINISHED;
};
}
}
4-2. Job Parameter 자동 타임스탬프 사용하기
/**
* Job Parameter 자동 타임스템프 사용하기
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch04_2 {
public static class ParameterValidator implements JobParametersValidator{
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
System.out.println("ParameterValidator.validate");
System.out.println("parameters = " + parameters);
System.out.println("parameters foo = " + parameters.getString("foo"));
String foo = parameters.getString("foo");
if (!StringUtils.hasText(foo))
throw new JobParametersInvalidException("foo must not empty");
else if (!StringUtils.endsWithIgnoreCase(foo, "bar"))
throw new JobParametersInvalidException("foo must is bar");
}
}
public static class DailyJobTimeStamper implements JobParametersIncrementer{
@Override
public JobParameters getNext(JobParameters parameters) {
return new JobParametersBuilder(parameters)
.addDate("currentDate", new Date())
.toJobParameters();
}
}
@Bean
public CompositeJobParametersValidator validator() {
CompositeJobParametersValidator validator = new CompositeJobParametersValidator();
DefaultJobParametersValidator defaultJobParametersValidator = new DefaultJobParametersValidator();
defaultJobParametersValidator.setRequiredKeys(new String[]{"foo"});
defaultJobParametersValidator.setOptionalKeys(new String[]{"name", "currentDate"});
defaultJobParametersValidator.afterPropertiesSet();
validator.setValidators(
Arrays.asList(new ParameterValidator(), defaultJobParametersValidator)
);
return validator;
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.validator(validator())
.incrementer(new DailyJobTimeStamper())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null, null, null))
.build();
}
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}")String foo,
@Value("#{jobParameters['name']}")String name,
@Value("#{jobParameters['currentDate']}")Long currentDate) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
System.out.println("name = " + name);
System.out.println("currentDate = " + new Date(currentDate));
return RepeatStatus.FINISHED;
};
}
}
5-1. Job Listener - JobExecutionListener 구현
/**
* Job Listener 잡 리스너 사용하기 JobExecutionListener 구현
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch05_1 {
public static class JobLoggerListener implements JobExecutionListener {
private static String START_MESSAGE = "%s is beginning execution";
private static String END_MESSAGE = "%s has completed with the status %s";
@Override
public void beforeJob(JobExecution jobExecution) {
System.out.println(String.format(START_MESSAGE, jobExecution.getJobInstance().getJobName()));
}
@Override
public void afterJob(JobExecution jobExecution) {
System.out.println(String.format(END_MESSAGE,
jobExecution.getJobInstance().getJobName(),
jobExecution.getStatus()));
}
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.listener(new JobLoggerListener())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null))
.build();
}
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}")String foo) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
return RepeatStatus.FINISHED;
};
}
}
5-2. Job Listener - 애너테이션 사용하기
/**
* Job Listener 잡 리스너 사용하기 애너테이션 사용
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch05_2 {
public static class JobLoggerListener {
private static String START_MESSAGE = "%s is beginning execution";
private static String END_MESSAGE = "%s has completed with the status %s";
@BeforeJob
public void beforeJob(JobExecution jobExecution) {
System.out.println(String.format(START_MESSAGE, jobExecution.getJobInstance().getJobName()));
}
@AfterJob
public void afterJob(JobExecution jobExecution) {
System.out.println(String.format(END_MESSAGE,
jobExecution.getJobInstance().getJobName(),
jobExecution.getStatus()));
}
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.listener(JobListenerFactoryBean.getListener(new JobLoggerListener()))
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(helloWorldTasklet(null))
.build();
}
@StepScope
@Bean
public Tasklet helloWorldTasklet(@Value("#{jobParameters['foo']}")String foo) {
return (contribution, chunkContext) -> {
System.out.println("foo = " + foo);
return RepeatStatus.FINISHED;
};
}
}
6-1. Job ExecutionContext 조작하기 - Job 의 ExecutionContext 에 foo.value 추가하기
/**
* Job ExecutionContext 조작하기
* Job 의 ExecutionContext 에 foo.value 추가하기
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch06_1 {
public static class HelloWorld implements Tasklet {
private final static String HELLO_WORLD = "Hello, %s";
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String foo = (String) chunkContext.getStepContext()
.getJobParameters()
.get("foo");
ExecutionContext jobContext = chunkContext.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext();
jobContext.put("foo.value", foo);
System.out.println(String.format(HELLO_WORLD, foo));
return RepeatStatus.FINISHED;
}
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.listener(JobListenerFactoryBean.getListener(new Ch05_1.JobLoggerListener()))
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(new HelloWorld())
.build();
}
}
6-2. Job ExecutionContext 조작하기 - Step 의 ExecutionContext 에 foo.value 추가하기
/**
* Job ExecutionContext 조작하기
* Step 의 ExecutionContext 에 foo 추가하기
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch06_2 {
public static class HelloWorld implements Tasklet {
private final static String HELLO_WORLD = "Hello, %s";
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String foo = (String) chunkContext.getStepContext()
.getJobParameters()
.get("foo");
ExecutionContext stepContext = chunkContext.getStepContext()
.getStepExecution()
.getExecutionContext();
stepContext.put("foo.value", foo);
System.out.println(String.format(HELLO_WORLD, foo));
return RepeatStatus.FINISHED;
}
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.listener(JobListenerFactoryBean.getListener(new Ch05_1.JobLoggerListener()))
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(new HelloWorld())
.build();
}
}
6-3. 잡의 ExecutionContext에 name 키 승격하기
/**
* Job ExecutionContext 조작하기
* Job 의 ExecutionContext 에 foo 승격시키기
* =================> java -jar ./build/libs/demo-0.0.1-SNAPSHOT.jar foo=bar 실행
*/
@EnableBatchProcessing
@Configuration
@RequiredArgsConstructor
public class Ch06_3 {
public static class HelloWorld implements Tasklet {
private final static String HELLO_WORLD = "Hello, %s";
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String foo = (String) chunkContext.getStepContext()
.getJobParameters()
.get("foo");
ExecutionContext stepContext = chunkContext.getStepContext()
.getStepExecution()
.getExecutionContext();
stepContext.put("foo.value", foo);
System.out.println(String.format(HELLO_WORLD, foo));
return RepeatStatus.FINISHED;
}
}
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return this.jobBuilderFactory.get("basicJob")
.start(step1())
.next(step2())
.listener(JobListenerFactoryBean.getListener(new Ch05_1.JobLoggerListener()))
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet(new HelloWorld())
.listener(promotionListener())
.build();
}
@Bean
public StepExecutionListener promotionListener() {
ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
listener.setKeys(new String[]{"foo"});
return listener;
}
@Bean
public Step step2() {
return this.stepBuilderFactory.get("step2")
.tasklet((contribution, chunkContext) -> {
System.out.println(chunkContext.getStepContext().getJobParameters().get("foo"));
return RepeatStatus.FINISHED;
})
.build();
}
}
7-1. Step-Flow
@Configuration
@RequiredArgsConstructor
public class FlowStepConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job exampleJob() {
return jobBuilderFactory.get("exampleJob")
.start(startStep())
.on("FAILED") //startStep의 ExitStatus가 FAILED일 경우
.to(failOverStep()) //failOver Step을 실행 시킨다.
.on("*") //failOver Step의 결과와 상관없이
.to(writeStep()) //write Step을 실행 시킨다.
.on("*") //write Step의 결과와 상관없 이
.end() //Flow를 종료시킨다.
.from(startStep()) //startStep이 FAILED가 아니고
.on("COMPLETED") //COMPLETED일 경우
.to(processStep()) //process Step을 실행 시킨다
.on("*") //process Step의 결과와 상관없이
.to(writeStep()) // write Step을 실행 시킨다.
.on("*") //wrtie Step의 결과와 상관없이
.end() //Flow를 종료 시킨다.
.from(startStep()) //startStep의 결과가 FAILED, COMPLETED가 아닌
.on("*") //모든 경우
.to(writeStep()) //write Step을 실행시킨다.
.on("*") //write Step의 결과와 상관없이
.end() //Flow를 종료시킨다.
.end()
.build();
}
@Bean
public Step startStep() {
return stepBuilderFactory.get("startStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("FlowStepConfig.startStep");
String result = "COMPLETED";
//String result = "FAIL";
//String result = "UNKNOWN";
//Flow에서 on은 RepeatStatus가 아닌 ExitStatus를 바라본다.
if(result.equals("COMPLETED"))
contribution.setExitStatus(ExitStatus.COMPLETED);
else if(result.equals("FAIL"))
contribution.setExitStatus(ExitStatus.FAILED);
else if(result.equals("UNKNOWN"))
contribution.setExitStatus(ExitStatus.UNKNOWN);
return RepeatStatus.FINISHED;
})
.build();
}
@Bean
public Step failOverStep(){
return stepBuilderFactory.get("nextStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("FlowStepConfig.failOverStep");
return RepeatStatus.FINISHED;
})
.build();
}
@Bean
public Step processStep(){
return stepBuilderFactory.get("processStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("FlowStepConfig.processStep");
return RepeatStatus.FINISHED;
})
.build();
}
@Bean
public Step writeStep(){
return stepBuilderFactory.get("writeStep")
.tasklet((contribution, chunkContext) -> {
System.out.println("FlowStepConfig.writeStep");
return RepeatStatus.FINISHED;
})
.build();
}
}
728x90
'Batch' 카테고리의 다른 글
Spring Batch 를 Jenkins 를 이용해 간단하게 사용해보자 (0) | 2022.07.08 |
---|---|
Spring Batch A 부터 Z 까지 (0) | 2022.07.08 |
Quartz 사용해보기 (0) | 2022.04.25 |
스프링 배치 스텝 Step (0) | 2022.04.04 |
스프링 배치 잡 Job (0) | 2022.04.04 |