Update TaskLifecycleListener to use SmartLifecycle
This commit changes the starting point of a task from the point when the ApplicationContext issues the ContextRefreshedEvent to SmartLifecycle#start. This is a more accurate point of start for a task in that all beans should now be available. It also allows us to clean up many ApplicationContext hacks that were present to get around the fact that many beans were not ready when a Task was attempting to begin. Resolves spring-cloud/spring-cloud-task#107
This commit is contained in:
committed by
Glenn Renfro
parent
7c8fc5f50e
commit
f35f8ef52d
@@ -15,14 +15,19 @@
|
||||
*/
|
||||
package org.springframework.cloud.task.batch.configuration;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.task.batch.listener.TaskBatchExecutionListener;
|
||||
import org.springframework.cloud.task.configuration.EnableTask;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.cloud.task.repository.TaskExplorer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Provides auto configuration for the {@link TaskBatchExecutionListener}.
|
||||
@@ -39,9 +44,24 @@ public class TaskBatchAutoConfiguration {
|
||||
return new TaskBatchExecutionListenerBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TaskBatchExecutionListenerFactoryBean taskBatchExecutionListener(ConfigurableApplicationContext context) {
|
||||
return new TaskBatchExecutionListenerFactoryBean(context);
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(name = "taskBatchExecutionListener")
|
||||
public static class TaskBatchExecutionListenerAutoconfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Collection<DataSource> dataSources;
|
||||
|
||||
@Bean
|
||||
public TaskBatchExecutionListenerFactoryBean taskBatchExecutionListener(TaskExplorer taskExplorer) {
|
||||
if(!CollectionUtils.isEmpty(dataSources) && dataSources.size() == 1) {
|
||||
return new TaskBatchExecutionListenerFactoryBean(dataSources.iterator().next(), taskExplorer);
|
||||
}
|
||||
else if(CollectionUtils.isEmpty(dataSources)) {
|
||||
return new TaskBatchExecutionListenerFactoryBean(null, taskExplorer);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Expected one datasource and found " + dataSources.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,7 @@ import org.springframework.cloud.task.batch.listener.support.MapTaskBatchDao;
|
||||
import org.springframework.cloud.task.repository.TaskExplorer;
|
||||
import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao;
|
||||
import org.springframework.cloud.task.repository.support.SimpleTaskExplorer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} for a {@link TaskBatchExecutionListener}. Provides a jdbc based
|
||||
@@ -41,19 +38,15 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class TaskBatchExecutionListenerFactoryBean implements FactoryBean<TaskBatchExecutionListener> {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private TaskBatchExecutionListener listener;
|
||||
|
||||
private String dataSourceName;
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* @param context the current application context
|
||||
*/
|
||||
public TaskBatchExecutionListenerFactoryBean(ConfigurableApplicationContext context) {
|
||||
Assert.notNull(context, "A ConfigurableApplicationContext is required");
|
||||
private TaskExplorer taskExplorer;
|
||||
|
||||
this.context = context;
|
||||
public TaskBatchExecutionListenerFactoryBean(DataSource dataSource, TaskExplorer taskExplorer) {
|
||||
this.dataSource = dataSource;
|
||||
this.taskExplorer = taskExplorer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,24 +54,11 @@ public class TaskBatchExecutionListenerFactoryBean implements FactoryBean<TaskBa
|
||||
if(listener != null){
|
||||
return listener;
|
||||
}
|
||||
if(this.context.getBeanNamesForType(DataSource.class).length == 0) {
|
||||
if(this.dataSource == null) {
|
||||
this.listener = new TaskBatchExecutionListener(getMapTaskBatchDao());
|
||||
}
|
||||
else {
|
||||
DataSource dataSource;
|
||||
|
||||
if(StringUtils.hasText(this.dataSourceName)) {
|
||||
dataSource = (DataSource) this.context.getBean(this.dataSourceName);
|
||||
}
|
||||
else {
|
||||
if(this.context.getBeanNamesForType(DataSource.class).length == 1) {
|
||||
dataSource = this.context.getBean(DataSource.class);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unable to determine what DataSource to use");
|
||||
}
|
||||
}
|
||||
this.listener = new TaskBatchExecutionListener(new JdbcTaskBatchDao(dataSource));
|
||||
this.listener = new TaskBatchExecutionListener(new JdbcTaskBatchDao(this.dataSource));
|
||||
}
|
||||
|
||||
return listener;
|
||||
@@ -94,27 +74,21 @@ public class TaskBatchExecutionListenerFactoryBean implements FactoryBean<TaskBa
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setDataSourceName(String dataSourceName) {
|
||||
this.dataSourceName = dataSourceName;
|
||||
}
|
||||
|
||||
private MapTaskBatchDao getMapTaskBatchDao() throws Exception {
|
||||
Field taskExecutionDaoField = ReflectionUtils.findField(SimpleTaskExplorer.class, "taskExecutionDao");
|
||||
taskExecutionDaoField.setAccessible(true);
|
||||
|
||||
MapTaskExecutionDao taskExecutionDao;
|
||||
|
||||
TaskExplorer taskExplorer = this.context.getBean(TaskExplorer.class);
|
||||
|
||||
if(AopUtils.isJdkDynamicProxy(taskExplorer)) {
|
||||
SimpleTaskExplorer dereferencedTaskRepository = (SimpleTaskExplorer) ((Advised) taskExplorer).getTargetSource().getTarget();
|
||||
if(AopUtils.isJdkDynamicProxy(this.taskExplorer)) {
|
||||
SimpleTaskExplorer dereferencedTaskRepository = (SimpleTaskExplorer) ((Advised) this.taskExplorer).getTargetSource().getTarget();
|
||||
|
||||
taskExecutionDao =
|
||||
(MapTaskExecutionDao) ReflectionUtils.getField(taskExecutionDaoField, dereferencedTaskRepository);
|
||||
}
|
||||
else {
|
||||
taskExecutionDao =
|
||||
(MapTaskExecutionDao) ReflectionUtils.getField(taskExecutionDaoField, taskExplorer);
|
||||
(MapTaskExecutionDao) ReflectionUtils.getField(taskExecutionDaoField, this.taskExplorer);
|
||||
}
|
||||
|
||||
return new MapTaskBatchDao(taskExecutionDao.getBatchJobAssociations());
|
||||
|
||||
@@ -18,11 +18,14 @@ package org.springframework.cloud.task.batch.listener;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
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.DefaultBatchConfigurer;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
@@ -35,21 +38,28 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
|
||||
import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfiguration;
|
||||
import org.springframework.cloud.task.batch.configuration.TaskBatchExecutionListenerFactoryBean;
|
||||
import org.springframework.cloud.task.configuration.DefaultTaskConfigurer;
|
||||
import org.springframework.cloud.task.configuration.EnableTask;
|
||||
import org.springframework.cloud.task.configuration.TaskConfigurer;
|
||||
import org.springframework.cloud.task.repository.TaskExecution;
|
||||
import org.springframework.cloud.task.repository.TaskExplorer;
|
||||
import org.springframework.cloud.task.repository.support.TaskRepositoryInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Michael Minella
|
||||
*/
|
||||
public class BatchTaskExecutionListenerTests {
|
||||
public class TaskBatchExecutionListenerTests {
|
||||
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@@ -62,7 +72,29 @@ public class BatchTaskExecutionListenerTests {
|
||||
|
||||
@Test
|
||||
public void testAutobuiltDataSource() {
|
||||
this.applicationContext = SpringApplication.run(new Object[] {JobConfiguration.class, PropertyPlaceholderAutoConfiguration.class, EmbeddedDataSourceConfiguration.class, TaskBatchAutoConfiguration.class, BatchAutoConfiguration.class, TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
this.applicationContext = SpringApplication.run(new Object[] {JobConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
EmbeddedDataSourceConfiguration.class,
|
||||
BatchAutoConfiguration.class,
|
||||
TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
|
||||
TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class);
|
||||
|
||||
Page<TaskExecution> page = taskExplorer.findTaskExecutionsByName("application", new PageRequest(0, 1));
|
||||
|
||||
Set<Long> jobExecutionIds = taskExplorer.getJobExecutionIdsByTaskExecutionId(page.iterator().next().getExecutionId());
|
||||
|
||||
assertEquals(1, jobExecutionIds.size());
|
||||
assertEquals(1, taskExplorer.getTaskExecution(jobExecutionIds.iterator().next()).getExecutionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleDataSources() {
|
||||
this.applicationContext = SpringApplication.run(new Object[] {JobConfigurationMultipleDataSources.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
EmbeddedDataSourceConfiguration.class,
|
||||
BatchAutoConfiguration.class,
|
||||
TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
|
||||
TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class);
|
||||
|
||||
@@ -76,7 +108,11 @@ public class BatchTaskExecutionListenerTests {
|
||||
|
||||
@Test
|
||||
public void testAutobuiltDataSourceNoJob() {
|
||||
this.applicationContext = SpringApplication.run(new Object[] {NoJobConfiguration.class, PropertyPlaceholderAutoConfiguration.class, EmbeddedDataSourceConfiguration.class, TaskBatchAutoConfiguration.class, BatchAutoConfiguration.class, TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
this.applicationContext = SpringApplication.run(new Object[] {NoJobConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
EmbeddedDataSourceConfiguration.class,
|
||||
BatchAutoConfiguration.class,
|
||||
TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
|
||||
TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class);
|
||||
|
||||
@@ -89,7 +125,10 @@ public class BatchTaskExecutionListenerTests {
|
||||
|
||||
@Test
|
||||
public void testMapBased() {
|
||||
this.applicationContext = SpringApplication.run(new Object[] {JobConfiguration.class, PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
this.applicationContext = SpringApplication.run(new Object[] {JobConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
BatchAutoConfiguration.class,
|
||||
TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
|
||||
TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class);
|
||||
|
||||
@@ -103,7 +142,10 @@ public class BatchTaskExecutionListenerTests {
|
||||
|
||||
@Test
|
||||
public void testMultipleJobs() {
|
||||
this.applicationContext = SpringApplication.run(new Object[] {MultipleJobConfiguration.class, PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
this.applicationContext = SpringApplication.run(new Object[] {MultipleJobConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
BatchAutoConfiguration.class,
|
||||
TaskBatchAutoConfiguration.class}, new String[0]);
|
||||
|
||||
TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class);
|
||||
|
||||
@@ -149,6 +191,72 @@ public class BatchTaskExecutionListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
@EnableTask
|
||||
public static class JobConfigurationMultipleDataSources {
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public DataSource myDataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
.setType(EmbeddedDatabaseType.H2)
|
||||
.setName("myDataSource");
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource incorrectDataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
.setType(EmbeddedDatabaseType.H2)
|
||||
.setName("incorrectDataSource");
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskBatchExecutionListenerFactoryBean taskBatchExecutionListener(TaskExplorer taskExplorer) {
|
||||
return new TaskBatchExecutionListenerFactoryBean(myDataSource(), taskExplorer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskConfigurer taskConfigurer() {
|
||||
return new DefaultTaskConfigurer(myDataSource());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskRepositoryInitializer taskRepositoryInitializer() {
|
||||
TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer();
|
||||
|
||||
taskRepositoryInitializer.setDataSource(myDataSource());
|
||||
|
||||
return taskRepositoryInitializer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultBatchConfigurer batchConfigurer() {
|
||||
return new DefaultBatchConfigurer(myDataSource());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
@EnableTask
|
||||
Reference in New Issue
Block a user