Allows failed batch apps to set exit code of task app.

resolves #201
 Please enter the commit message for your changes. Lines starting

Cleanup Removed taskProperties from TaskBatchAutoConfiguration
This commit is contained in:
Glenn Renfro
2018-01-22 10:53:36 -05:00
committed by Michael Minella
parent b6f6d68385
commit cd4dbe6a93
12 changed files with 870 additions and 13 deletions

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2018 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.cloud.task.batch.configuration;
import org.junit.Test;
import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration;
import org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.task.batch.handler.TaskJobLauncherCommandLineRunner;
import org.springframework.cloud.task.batch.listener.TaskBatchExecutionListenerTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Glenn Renfro
*/
public class TaskJobLauncherAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().
withUserConfiguration(TaskBatchExecutionListenerTests.JobConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedDataSourceConfiguration.class,
BatchAutoConfiguration.class,
TaskJobLauncherAutoConfiguration.class);
@Test
public void testAutoBuiltDataSourceWithTaskJobLauncherCLR() {
this.contextRunner.
withPropertyValues("spring.cloud.task.batch.commandLineRunnerEnabled=true").
run(context -> {
assertThat(context).hasSingleBean(TaskJobLauncherCommandLineRunner.class);
assertThat(context).doesNotHaveBean(JobLauncherCommandLineRunner.class);
});
}
@Test
public void testAutoBuiltDataSourceWithTaskJobLauncherCLRDisabled() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(JobLauncherCommandLineRunner.class);
assertThat(context).doesNotHaveBean(TaskJobLauncherCommandLineRunner.class);
});
}
}

View File

@@ -0,0 +1,210 @@
/*
* Copyright 2018 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.cloud.task.batch.handler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.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.step.tasklet.Tasklet;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.task.listener.TaskException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Glenn Renfro
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {TaskJobLauncherCommandLineRunnerCoreTests.BatchConfiguration.class})
public class TaskJobLauncherCommandLineRunnerCoreTests {
@Autowired
private JobRepository jobRepository;
@Autowired
private JobLauncher jobLauncher;
@Autowired
private JobExplorer jobExplorer;
@Autowired
PlatformTransactionManager transactionManager;
private TaskJobLauncherCommandLineRunner runner;
private JobBuilderFactory jobs;
private StepBuilderFactory steps;
private Job job;
private Step step;
@Before
public void init() {
this.jobs = new JobBuilderFactory(this.jobRepository);
this.steps = new StepBuilderFactory(this.jobRepository, this.transactionManager);
Tasklet tasklet = (contribution, chunkContext) -> null;
this.step = this.steps.get("step").tasklet(tasklet).build();
this.job = this.jobs.get("job").start(this.step).build();
this.runner = new TaskJobLauncherCommandLineRunner(this.jobLauncher, this.jobExplorer);
}
@DirtiesContext
@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);
}
@DirtiesContext
@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);
}
@DirtiesContext
@Test
public void retryFailedExecution() throws Exception {
this.job = this.jobs.get("job")
.start(this.steps.get("step").tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
runFailedJob(new JobParameters());
runFailedJob(new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
}
@DirtiesContext
@Test
public void retryFailedExecutionOnNonRestartableJob() throws Exception {
this.job = this.jobs.get("job").preventRestart()
.start(this.steps.get("step").tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
runFailedJob(new JobParameters());
runFailedJob(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);
}
@DirtiesContext
@Test
public void retryFailedExecutionWithNonIdentifyingParameters() throws Exception {
this.job = this.jobs.get("job")
.start(this.steps.get("step").tasklet(throwingTasklet()).build())
.incrementer(new RunIdIncrementer()).build();
JobParameters jobParameters = new JobParametersBuilder().addLong("id", 1L, false)
.addLong("foo", 2L, false).toJobParameters();
runFailedJob(new JobParameters());
runFailedJob(new JobParameters());
assertThat(this.jobExplorer.getJobInstances("job", 0, 100)).hasSize(1);
}
private Tasklet throwingTasklet() {
return (contribution, chunkContext) -> {
throw new RuntimeException("Planned");
};
}
private void runFailedJob(JobParameters jobParameters) throws Exception {
boolean isExceptionThrown = false;
try {
this.runner.execute(this.job, new JobParameters());
}
catch (TaskException taskException) {
isExceptionThrown = true;
}
assertThat(isExceptionThrown).isTrue();
}
@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() {
return this.jobRepository;
}
@Override
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
@Override
public JobLauncher getJobLauncher() {
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();
}
}
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright 2018 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.cloud.task.batch.handler;
import java.util.Set;
import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.StepContribution;
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.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration;
import org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfiguration;
import org.springframework.cloud.task.batch.configuration.TaskJobLauncherAutoConfiguration;
import org.springframework.cloud.task.configuration.EnableTask;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.cloud.task.repository.TaskExplorer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/**
* @author Glenn Renfro
*/
public class TaskJobLauncherCommandLineRunnerTests {
private ConfigurableApplicationContext applicationContext;
@After
public void tearDown() {
if (this.applicationContext != null) {
this.applicationContext.close();
}
}
@Test
public void testTaskJobLauncherCLRSuccessFail() {
String[] enabledArgs = new String[] { "--spring.cloud.task.batch.commandLineRunnerEnabled=true" };
boolean isExceptionThrown = false;
try {
this.applicationContext = SpringApplication
.run(new Class[] { TaskJobLauncherCommandLineRunnerTests.JobWithFailureConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedDataSourceConfiguration.class,
BatchAutoConfiguration.class,
TaskBatchAutoConfiguration.class,
TaskJobLauncherAutoConfiguration.class }, enabledArgs);
}
catch (IllegalStateException exception) {
isExceptionThrown = true;
}
assertThat(isExceptionThrown).isTrue();
}
@Test
public void testTaskJobLauncherPickOneJob() {
String[] enabledArgs = new String[] {
"--spring.cloud.task.batch.commandLineRunnerEnabled=true",
"--spring.cloud.task.batch.jobNames=jobSucceed" };
boolean isExceptionThrown = false;
try {
this.applicationContext = SpringApplication
.run(new Class[] { TaskJobLauncherCommandLineRunnerTests.JobWithFailureConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedDataSourceConfiguration.class,
BatchAutoConfiguration.class,
TaskBatchAutoConfiguration.class,
TaskJobLauncherAutoConfiguration.class }, enabledArgs);
}
catch (IllegalStateException exception) {
isExceptionThrown = true;
}
assertThat(isExceptionThrown).isFalse();
validateContext();
}
@Test
public void testCommandLineRunnerSetToFalse() {
String[] enabledArgs = new String[] { };
this.applicationContext = SpringApplication
.run(new Class[] { TaskJobLauncherCommandLineRunnerTests.JobConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
EmbeddedDataSourceConfiguration.class,
BatchAutoConfiguration.class,
TaskBatchAutoConfiguration.class,
TaskJobLauncherAutoConfiguration.class }, enabledArgs);
validateContext();
assertThat(applicationContext.getBean(JobLauncherCommandLineRunner.class)).isNotNull();
boolean exceptionThrown = false;
try {
applicationContext.getBean(TaskJobLauncherCommandLineRunner.class);
}
catch (NoSuchBeanDefinitionException exception) {
exceptionThrown = true;
}
assertThat(exceptionThrown).isTrue();
validateContext();
}
private void validateContext() {
TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class);
Page<TaskExecution> page = taskExplorer.findTaskExecutionsByName("application", PageRequest.of(0, 1));
Set<Long> jobExecutionIds = taskExplorer
.getJobExecutionIdsByTaskExecutionId(page.iterator().next().getExecutionId());
assertThat(jobExecutionIds.size()).isEqualTo(1);
assertThat(taskExplorer.getTaskExecution(jobExecutionIds.iterator().next()).getExecutionId()).isEqualTo(1);
}
@Configuration
@EnableBatchProcessing
@EnableTask
public static class JobConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job job() {
return jobBuilderFactory.get("job")
.start(stepBuilderFactory.get("step1").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
throws Exception {
System.out.println("Executed");
return RepeatStatus.FINISHED;
}
}).build())
.build();
}
}
@Configuration
@EnableBatchProcessing
@EnableTask
public static class JobWithFailureConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job jobFail() {
return jobBuilderFactory.get("jobA")
.start(stepBuilderFactory.get("step1").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext)
throws Exception {
System.out.println("Executed");
throw new IllegalStateException("WHOOPS");
}
}).build())
.build();
}
@Bean
public Job jobFun() {
return jobBuilderFactory.get("jobSucceed")
.start(stepBuilderFactory.get("step1Succeed").tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext)
throws Exception {
System.out.println("Executed");
return RepeatStatus.FINISHED;
}
}).build())
.build();
}
}
}

View File

@@ -65,7 +65,7 @@ import static org.junit.Assert.assertEquals;
*/
public class TaskBatchExecutionListenerTests {
public static final String[] ARGS = new String[] {"--spring.cloud.task.closecontext_enable=false"};
public static final String[] ARGS = new String[] {};
private ConfigurableApplicationContext applicationContext;