/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.batch;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
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.explore.JobExplorer;
import org.springframework.batch.core.explore.support.MapJobExplorerFactoryBean;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link JobLauncherCommandLineRunner}.
*
* @author Dave Syer
* @author Jean-Pierre Bergamin
*/
public class JobLauncherCommandLineRunnerTests {
private JobLauncherCommandLineRunner runner;
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
private JobExplorer jobExplorer;
private JobLauncher jobLauncher;
private JobBuilderFactory jobs;
private StepBuilderFactory steps;
private Job job;
private Step step;
@Before
public void init() throws Exception {
this.context.register(BatchConfiguration.class);
this.context.refresh();
JobRepository jobRepository = this.context.getBean(JobRepository.class);
this.jobLauncher = this.context.getBean(JobLauncher.class);
this.jobs = new JobBuilderFactory(jobRepository);
PlatformTransactionManager transactionManager = this.context
.getBean(PlatformTransactionManager.class);
this.steps = new StepBuilderFactory(jobRepository, transactionManager);
this.step = this.steps.get("step").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
return null;
}
}).build();
this.job = this.jobs.get("job").start(this.step).build();
this.jobExplorer = this.context.getBean(JobExplorer.class);
this.runner = new JobLauncherCommandLineRunner(this.jobLauncher,
this.jobExplorer);
this.context.getBean(BatchConfiguration.class).clear();
}
@Test
public void basicExecution() throws Exception {
this.runner.execute(this.job, new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
this.runner.execute(this.job,
new JobParametersBuilder().addLong("id", 1L).toJobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
}
@Test
public void incrementExistingExecution() throws Exception {
this.job = this.jobs.get("job").start(this.step)
.incrementer(new RunIdIncrementer()).build();
this.runner.execute(this.job, new JobParameters());
this.runner.execute(this.job, new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
}
@Test
public void retryFailedExecution() throws Exception {
this.job = this.jobs.get("job")
.start(this.steps.get("step").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
throw new RuntimeException("Planned");
}
}).build()).incrementer(new RunIdIncrementer()).build();
this.runner.execute(this.job, new JobParameters());
this.runner.execute(this.job, new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
}
@Test
public void retryFailedExecutionOnNonRestartableJob() throws Exception {
this.job = this.jobs.get("job").preventRestart()
.start(this.steps.get("step").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
throw new RuntimeException("Planned");
}
}).build()).incrementer(new RunIdIncrementer()).build();
this.runner.execute(this.job, new JobParameters());
this.runner.execute(this.job, new JobParameters());
// A failed job that is not restartable does not re-use the job params of
// the last execution, but creates a new job instance when running it again.
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(2);
}
@Test
public void retryFailedExecutionWithNonIdentifyingParameters() throws Exception {
this.job = this.jobs.get("job")
.start(this.steps.get("step").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
throw new RuntimeException("Planned");
}
}).build()).incrementer(new RunIdIncrementer()).build();
JobParameters jobParameters = new JobParametersBuilder().addLong("id", 1L, false)
.addLong("foo", 2L, false).toJobParameters();
this.runner.execute(this.job, jobParameters);
this.runner.execute(this.job, jobParameters);
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
}
@Configuration
@EnableBatchProcessing
protected static class BatchConfiguration implements BatchConfigurer {
private ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
private JobRepository jobRepository;
private MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(
this.transactionManager);
public BatchConfiguration() throws Exception {
this.jobRepository = this.jobRepositoryFactory.getObject();
}
public void clear() {
this.jobRepositoryFactory.clear();
}
@Override
public JobRepository getJobRepository() throws Exception {
return this.jobRepository;
}
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.transactionManager;
}
@Override
public JobLauncher getJobLauncher() throws Exception {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(this.jobRepository);
launcher.setTaskExecutor(new SyncTaskExecutor());
return launcher;
}
@Override
public JobExplorer getJobExplorer() throws Exception {
return new MapJobExplorerFactoryBean(this.jobRepositoryFactory).getObject();
}
}
}