diff --git a/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchAutoConfiguration.java b/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchAutoConfiguration.java index 3be0bcff..607e766c 100644 --- a/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchAutoConfiguration.java +++ b/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchAutoConfiguration.java @@ -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 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()); + } + } } } diff --git a/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchExecutionListenerFactoryBean.java b/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchExecutionListenerFactoryBean.java index 8ab333d4..9c47f5f7 100644 --- a/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchExecutionListenerFactoryBean.java +++ b/spring-cloud-task-batch/src/main/java/org/springframework/cloud/task/batch/configuration/TaskBatchExecutionListenerFactoryBean.java @@ -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 { - 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 page = taskExplorer.findTaskExecutionsByName("application", new PageRequest(0, 1)); + + Set 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 diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java index 066773b1..1fad9f5f 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java @@ -26,7 +26,6 @@ import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao; import org.springframework.cloud.task.repository.support.SimpleTaskExplorer; import org.springframework.cloud.task.repository.support.SimpleTaskRepository; import org.springframework.cloud.task.repository.support.TaskExecutionDaoFactoryBean; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @@ -51,14 +50,25 @@ public class DefaultTaskConfigurer implements TaskConfigurer { private PlatformTransactionManager transactionManager; - private ConfigurableApplicationContext context; - private TaskExecutionDaoFactoryBean taskExecutionDaoFactoryBean; - public DefaultTaskConfigurer(ConfigurableApplicationContext context) { - this.context = context; + private DataSource dataSource; + + /** + * @param dataSource references the {@link DataSource} to be used as the Task + * repository. If none is provided, a Map will be used (not recommended for + * production use. + */ + public DefaultTaskConfigurer(DataSource dataSource) { + this.dataSource = dataSource; + + if(this.dataSource != null) { + this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(this.dataSource); + } + else { + this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(); + } - this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(this.context); this.taskRepository = new SimpleTaskRepository(this.taskExecutionDaoFactoryBean); this.taskExplorer = new SimpleTaskExplorer(this.taskExecutionDaoFactoryBean); } @@ -77,7 +87,7 @@ public class DefaultTaskConfigurer implements TaskConfigurer { public PlatformTransactionManager getTransactionManager() { if(this.transactionManager == null) { if(isDataSourceAvailable()) { - this.transactionManager = new DataSourceTransactionManager(this.context.getBean(DataSource.class)); + this.transactionManager = new DataSourceTransactionManager(this.dataSource); } else { this.transactionManager = new ResourcelessTransactionManager(); @@ -88,6 +98,6 @@ public class DefaultTaskConfigurer implements TaskConfigurer { } private boolean isDataSourceAvailable() { - return this.context.getBeanNamesForType(DataSource.class).length == 1; + return this.dataSource != null; } } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java index 36d121b4..a1fd8208 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java @@ -17,7 +17,6 @@ package org.springframework.cloud.task.configuration; import java.util.Collection; - import javax.annotation.PostConstruct; import javax.sql.DataSource; @@ -27,8 +26,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.cloud.task.listener.TaskLifecycleListener; -import org.springframework.cloud.task.listener.annotation.TaskListenerExecutor; -import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorFactory; +import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorFactoryBean; import org.springframework.cloud.task.repository.TaskExplorer; import org.springframework.cloud.task.repository.TaskNameResolver; import org.springframework.cloud.task.repository.TaskRepository; @@ -39,6 +37,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.util.CollectionUtils; /** * Base {@code Configuration} class providing common structure for enabling and using @@ -54,6 +53,9 @@ public class SimpleTaskConfiguration { protected static final Log logger = LogFactory.getLog(SimpleTaskConfiguration.class); + @Autowired(required = false) + private Collection dataSources; + @Autowired private ConfigurableApplicationContext context; @@ -62,34 +64,40 @@ public class SimpleTaskConfiguration { private boolean initialized = false; - private TaskConfigurer configurer; + private TaskRepository taskRepository; + + private TaskLifecycleListener taskLifecycleListener; + + private TaskListenerExecutorFactoryBean taskListenerExecutorFactoryBean; + + private PlatformTransactionManager platformTransactionManager; + + private TaskExplorer taskExplorer; @Bean public TaskRepository taskRepository(){ - return this.configurer.getTaskRepository(); + return this.taskRepository; } @Bean public TaskLifecycleListener taskLifecycleListener() { - return new TaskLifecycleListener(taskRepository(), taskNameResolver(), this.applicationArguments); + return this.taskLifecycleListener; } @Bean - public TaskListenerExecutor taskListenerExecutor(ConfigurableApplicationContext context) throws Exception - { - TaskListenerExecutorFactory taskListenerExecutorFactory = - new TaskListenerExecutorFactory(context); - return taskListenerExecutorFactory.getObject(); + public TaskListenerExecutorFactoryBean taskListenerExecutor() + throws Exception { + return this.taskListenerExecutorFactoryBean; } @Bean public PlatformTransactionManager transactionManager() { - return this.configurer.getTransactionManager(); + return this.platformTransactionManager; } @Bean public TaskExplorer taskExplorer() { - return this.configurer.getTaskExplorer(); + return this.taskExplorer; } @Bean @@ -101,8 +109,9 @@ public class SimpleTaskConfiguration { public TaskRepositoryInitializer taskRepositoryInitializer() { TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer(); - if(this.context.getBeanNamesForType(DataSource.class).length == 1) { - taskRepositoryInitializer.setDataSource(context.getBean(DataSource.class)); + if(!CollectionUtils.isEmpty(this.dataSources) && this.dataSources.size() == 1) { + DataSource next = this.dataSources.iterator().next(); + taskRepositoryInitializer.setDataSource(next); } return taskRepositoryInitializer; @@ -112,40 +121,56 @@ public class SimpleTaskConfiguration { * Determines the {@link TaskConfigurer} to use. */ @PostConstruct - protected void initialize() { + protected void initialize() throws Exception { if (initialized) { return; } - logger.debug("Getting Task Configurer"); - if (configurer == null) { - configurer = getDefaultConfigurer(context.getBeansOfType(TaskConfigurer.class).values()); - } + + TaskConfigurer taskConfigurer = getDefaultConfigurer(); + logger.debug(String.format("Using %s TaskConfigurer", - configurer.getClass().getName())); + taskConfigurer.getClass().getName())); + + this.taskRepository = taskConfigurer.getTaskRepository(); + this.taskListenerExecutorFactoryBean = new TaskListenerExecutorFactoryBean(context); + this.platformTransactionManager = taskConfigurer.getTransactionManager(); + this.taskExplorer = taskConfigurer.getTaskExplorer(); + + this.taskLifecycleListener = new TaskLifecycleListener(this.taskRepository, taskNameResolver(), this.applicationArguments); + initialized = true; } - private TaskConfigurer getDefaultConfigurer(Collection configurers) { - verifyEnvironment(configurers); - if (configurers == null || configurers.isEmpty()) { - this.configurer = new DefaultTaskConfigurer(this.context); - return this.configurer; + private TaskConfigurer getDefaultConfigurer() { + verifyEnvironment(); + + int configurers = this.context.getBeanNamesForType(TaskConfigurer.class).length; + + if (configurers < 1) { + if(!CollectionUtils.isEmpty(this.dataSources) && this.dataSources.size() == 1) { + return new DefaultTaskConfigurer(this.dataSources.iterator().next()); + } + else { + return new DefaultTaskConfigurer(null); + } + } + else { + if(configurers == 1) { + return this.context.getBean(TaskConfigurer.class); + } + else { + throw new IllegalStateException("Expected one TaskConfigurer but found " + configurers); + } } - this.configurer = configurers.iterator().next(); - return this.configurer; } - private void verifyEnvironment(Collection configurers){ + private void verifyEnvironment(){ + int configurers = this.context.getBeanNamesForType(TaskConfigurer.class).length; int dataSources = this.context.getBeanNamesForType(DataSource.class).length; - if (dataSources > 1) { + if(configurers == 0 && dataSources > 1) { throw new IllegalStateException("To use the default TaskConfigurer the context must contain no more than" + - "one DataSource, found " + dataSources); - } - if (configurers.size() > 1) { - throw new IllegalStateException( - "To use a custom TaskConfigurer the context must contain precisely one, found " - + configurers.size()); + " one DataSource, found " + dataSources); } } } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java index 3ca41db0..b2e75b8b 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 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. @@ -36,6 +36,7 @@ import org.springframework.cloud.task.repository.TaskNameResolver; import org.springframework.cloud.task.repository.TaskRepository; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.SmartLifecycle; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.util.Assert; @@ -58,7 +59,7 @@ import org.springframework.util.Assert; * * @author Michael Minella */ -public class TaskLifecycleListener implements ApplicationListener{ +public class TaskLifecycleListener implements ApplicationListener, SmartLifecycle { @Autowired(required = false) private Collection taskExecutionListeners; @@ -106,11 +107,7 @@ public class TaskLifecycleListener implements ApplicationListener { +public class TaskListenerExecutorFactoryBean implements FactoryBean { private final static Log logger = LogFactory.getLog(TaskListenerExecutor.class); @@ -55,7 +56,7 @@ public class TaskListenerExecutorFactory implements FactoryBean failedTaskInstances; - public TaskListenerExecutorFactory(ConfigurableApplicationContext context){ + public TaskListenerExecutorFactoryBean(ConfigurableApplicationContext context){ this.context = context; } @@ -161,5 +162,4 @@ public class TaskListenerExecutorFactory implements FactoryBean targetClass = AopProxyUtils.ultimateTargetClass(taskRepository); + + assertEquals(targetClass, SimpleTaskRepository.class); + } + + @Test(expected = BeanCreationException.class) + public void testMultipleConfigurers() { + this.context = new AnnotationConfigApplicationContext(MultipleConfigurers.class, + PropertyPlaceholderAutoConfiguration.class); + } + + @Configuration + @EnableTask + public static class MultipleConfigurers { + + @Bean + public TaskConfigurer taskConfigurer1() { + return new DefaultTaskConfigurer(null); + } + + @Bean + public TaskConfigurer taskConfigurer2() { + return new DefaultTaskConfigurer(null); + } + } + +} diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java index 3c056d7a..bad33af1 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java @@ -26,7 +26,6 @@ import org.springframework.cloud.task.repository.support.SimpleTaskExplorer; import org.springframework.cloud.task.repository.support.SimpleTaskRepository; import org.springframework.cloud.task.repository.support.TaskExecutionDaoFactoryBean; 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.core.io.ResourceLoader; @@ -46,9 +45,6 @@ public class TestConfiguration implements InitializingBean { @Autowired(required = false) private ResourceLoader resourceLoader; - @Autowired - private ConfigurableApplicationContext applicationContext; - private TaskExecutionDaoFactoryBean taskExecutionDaoFactoryBean; @Bean @@ -83,6 +79,11 @@ public class TestConfiguration implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { - this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(this.applicationContext); + if(this.dataSource != null) { + this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(this.dataSource); + } + else { + this.taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(); + } } } diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskExecutionListenerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskExecutionListenerTests.java index c292d8a8..9900860d 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskExecutionListenerTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskExecutionListenerTests.java @@ -16,24 +16,19 @@ package org.springframework.cloud.task.listener; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.util.ArrayList; import java.util.Date; import org.junit.After; import org.junit.Test; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.cloud.task.listener.annotation.AfterTask; import org.springframework.cloud.task.listener.annotation.BeforeTask; import org.springframework.cloud.task.listener.annotation.FailedTask; -import org.springframework.cloud.task.listener.annotation.TaskListenerExecutor; -import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorFactory; +import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorFactoryBean; import org.springframework.cloud.task.repository.TaskExecution; import org.springframework.cloud.task.util.TestDefaultConfiguration; import org.springframework.cloud.task.util.TestListener; @@ -43,6 +38,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + /** * Verifies that the TaskExecutionListener invocations occur at the appropriate task * lifecycle stages. @@ -203,10 +203,9 @@ public class TaskExecutionListenerTests { } @Bean - public TaskListenerExecutor taskListenerExecutor(ConfigurableApplicationContext context) throws Exception + public TaskListenerExecutorFactoryBean taskListenerExecutor(ConfigurableApplicationContext context) throws Exception { - TaskListenerExecutorFactory taskListenerExecutorFactory = new TaskListenerExecutorFactory(context); - return taskListenerExecutorFactory.getObject(); + return new TaskListenerExecutorFactoryBean(context); } public static class AnnotatedTaskListener extends TestListener { diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryMapTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryMapTests.java index 0eb6b0a4..7f465f7d 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryMapTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryMapTests.java @@ -27,9 +27,6 @@ import org.springframework.cloud.task.repository.TaskRepository; import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao; import org.springframework.cloud.task.util.TaskExecutionCreator; import org.springframework.cloud.task.util.TestVerifierUtils; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Configuration; import static org.springframework.test.util.AssertionErrors.assertTrue; @@ -43,8 +40,7 @@ public class SimpleTaskRepositoryMapTests { @Before public void setUp() { - ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(EmptyConfiguration.class); - this.taskRepository = new SimpleTaskRepository(new TaskExecutionDaoFactoryBean(context)); + this.taskRepository = new SimpleTaskRepository(new TaskExecutionDaoFactoryBean()); } @Test @@ -52,8 +48,7 @@ public class SimpleTaskRepositoryMapTests { TaskExecution expectedTaskExecution = TaskExecutionCreator.createAndStoreTaskExecutionNoParams(taskRepository); TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, - getSingleTaskExecutionFromMapRepository(taskRepository, - expectedTaskExecution.getExecutionId())); + getSingleTaskExecutionFromMapRepository(expectedTaskExecution.getExecutionId())); } @Test @@ -61,8 +56,7 @@ public class SimpleTaskRepositoryMapTests { TaskExecution expectedTaskExecution = TaskExecutionCreator.createAndStoreTaskExecutionWithParams(taskRepository); TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, - getSingleTaskExecutionFromMapRepository(taskRepository, - expectedTaskExecution.getExecutionId())); + getSingleTaskExecutionFromMapRepository(expectedTaskExecution.getExecutionId())); } @Test @@ -75,8 +69,7 @@ public class SimpleTaskRepositoryMapTests { TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, actualTaskExecution); } - private TaskExecution getSingleTaskExecutionFromMapRepository( - TaskRepository repository, long taskExecutionId){ + private TaskExecution getSingleTaskExecutionFromMapRepository(long taskExecutionId){ Map taskMap = ((MapTaskExecutionDao) ((SimpleTaskRepository)taskRepository).getTaskExecutionDao()).getTaskExecutions(); assertTrue("taskExecutionId must be in MapTaskExecutionRepository", @@ -91,7 +84,4 @@ public class SimpleTaskRepositoryMapTests { expectedTaskExecution.setExitCode(-1); TaskExecutionCreator.completeExecution(taskRepository, expectedTaskExecution); } - - @Configuration - public static class EmptyConfiguration{} } diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java index 800d2403..2227b59f 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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. @@ -16,11 +16,6 @@ package org.springframework.cloud.task.repository.support; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; - import javax.sql.DataSource; import org.junit.After; @@ -38,6 +33,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + /** * Verifies that task initialization occurs properly. * @@ -70,7 +70,7 @@ public class TaskDatabaseInitializerTests { @Test public void testNoDatabase() throws Exception { this.context = new AnnotationConfigApplicationContext(EmptyConfiguration.class); - SimpleTaskRepository repository = new SimpleTaskRepository(new TaskExecutionDaoFactoryBean(this.context)); + SimpleTaskRepository repository = new SimpleTaskRepository(new TaskExecutionDaoFactoryBean()); assertThat(repository.getTaskExecutionDao(), instanceOf(MapTaskExecutionDao.class)); MapTaskExecutionDao dao = (MapTaskExecutionDao) repository.getTaskExecutionDao(); assertEquals(0, dao.getTaskExecutions().size()); diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskExecutionDaoFactoryBeanTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskExecutionDaoFactoryBeanTests.java index 7fcb1383..941cd6ba 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskExecutionDaoFactoryBeanTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskExecutionDaoFactoryBeanTests.java @@ -15,9 +15,6 @@ */ package org.springframework.cloud.task.repository.support; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import javax.sql.DataSource; import org.junit.After; @@ -30,11 +27,13 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.GenericApplicationContext; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.test.util.ReflectionTestUtils; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * @author Michael Minella */ @@ -64,20 +63,6 @@ public class TaskExecutionDaoFactoryBeanTests { new TaskExecutionDaoFactoryBean(null); } - @Test - public void testMapTaskExecutionDaoWithAppContext() throws Exception { - this.context = new GenericApplicationContext(); - this.context.refresh(); - - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(this.context); - TaskExecutionDao taskExecutionDao = factoryBean.getObject(); - - assertTrue(taskExecutionDao instanceof MapTaskExecutionDao); - - TaskExecutionDao taskExecutionDao2 = factoryBean.getObject(); - - assertTrue(taskExecutionDao == taskExecutionDao2); - } @Test public void testMapTaskExecutionDaoWithoutAppContext() throws Exception { @@ -95,7 +80,9 @@ public class TaskExecutionDaoFactoryBeanTests { public void testDefaultDataSourceConfiguration() throws Exception { this.context = new AnnotationConfigApplicationContext(DefaultDataSourceConfiguration.class); - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(this.context); + DataSource dataSource = this.context.getBean(DataSource.class); + + TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(dataSource); TaskExecutionDao taskExecutionDao = factoryBean.getObject(); assertTrue(taskExecutionDao instanceof JdbcTaskExecutionDao); @@ -105,66 +92,14 @@ public class TaskExecutionDaoFactoryBeanTests { assertTrue(taskExecutionDao == taskExecutionDao2); } - @Test - public void testNonDefaultNameDataSourceConfiguration() throws Exception { - this.context = new AnnotationConfigApplicationContext(AlternativeDataSourceConfiguration.class); - - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(this.context); - TaskExecutionDao taskExecutionDao = factoryBean.getObject(); - - assertTrue(taskExecutionDao instanceof JdbcTaskExecutionDao); - - TaskExecutionDao taskExecutionDao2 = factoryBean.getObject(); - - assertTrue(taskExecutionDao == taskExecutionDao2); - } - - @Test(expected = IllegalArgumentException.class) - public void testMissingCustomDataSourceNameConfiguration() throws Exception { - ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(AlternativeDataSourceConfiguration.class); - - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(context); - factoryBean.setDataSourceName("wrongName"); - factoryBean.getObject(); - } - - @Test - public void testCustomDataSourceNameConfiguration() throws Exception { - this.context = new AnnotationConfigApplicationContext(AlternativeDataSourceConfiguration.class); - - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(this.context); - factoryBean.setDataSourceName("notDataSource"); - TaskExecutionDao taskExecutionDao = factoryBean.getObject(); - - assertTrue(taskExecutionDao instanceof JdbcTaskExecutionDao); - - TaskExecutionDao taskExecutionDao2 = factoryBean.getObject(); - - assertTrue(taskExecutionDao == taskExecutionDao2); - } - - @Test - public void testCustomDataSourceNameConfigurationWithMultipleDataSources() throws Exception { - this.context = new AnnotationConfigApplicationContext(MultipleDataSourceConfiguration.class); - - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(this.context); - factoryBean.setDataSourceName("useThisDataSource"); - JdbcTaskExecutionDao taskExecutionDao = (JdbcTaskExecutionDao) factoryBean.getObject(); - - Object usedDataSource = ReflectionTestUtils.getField(taskExecutionDao, "dataSource"); - - assertTrue(usedDataSource == this.context.getBean("useThisDataSource")); - - TaskExecutionDao taskExecutionDao2 = factoryBean.getObject(); - - assertTrue(taskExecutionDao == taskExecutionDao2); - } @Test public void testSettingTablePrefix() throws Exception { this.context = new AnnotationConfigApplicationContext(DefaultDataSourceConfiguration.class); - TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(this.context); + DataSource dataSource = this.context.getBean(DataSource.class); + + TaskExecutionDaoFactoryBean factoryBean = new TaskExecutionDaoFactoryBean(dataSource); factoryBean.setTablePrefix("foo_"); TaskExecutionDao taskExecutionDao = factoryBean.getObject(); @@ -180,35 +115,4 @@ public class TaskExecutionDaoFactoryBeanTests { return builder.build(); } } - - @Configuration - public static class AlternativeDataSourceConfiguration { - - @Bean - public DataSource notDataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2); - return builder.build(); - } - } - - @Configuration - public static class MultipleDataSourceConfiguration { - - @Bean - public DataSource useThisDataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() - .setType(EmbeddedDatabaseType.H2) - .setName("useThisDataSource"); - return builder.build(); - } - - @Bean - public DataSource dontUseThisDataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() - .setType(EmbeddedDatabaseType.H2) - .setName("dontUseThisDataSource"); - return builder.build(); - } - - } } diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java index 79aeabb4..616d2ca3 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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. @@ -16,6 +16,8 @@ package org.springframework.cloud.task.util; +import javax.sql.DataSource; + import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; @@ -35,6 +37,7 @@ import org.springframework.context.annotation.Configuration; * Initializes the beans needed to test default task behavior. * * @author Glenn Renfro + * @author Michael Minella */ @Configuration public class TestDefaultConfiguration implements InitializingBean { @@ -72,6 +75,12 @@ public class TestDefaultConfiguration implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { - this.factoryBean = new TaskExecutionDaoFactoryBean(this.context); + if(this.context.getBeanNamesForType(DataSource.class).length == 1){ + DataSource dataSource = this.context.getBean(DataSource.class); + this.factoryBean = new TaskExecutionDaoFactoryBean(dataSource); + } + else { + this.factoryBean = new TaskExecutionDaoFactoryBean(); + } } } diff --git a/spring-cloud-task-docs/src/main/asciidoc/features.adoc b/spring-cloud-task-docs/src/main/asciidoc/features.adoc index 974938b6..cf5780d8 100644 --- a/spring-cloud-task-docs/src/main/asciidoc/features.adoc +++ b/spring-cloud-task-docs/src/main/asciidoc/features.adoc @@ -33,13 +33,10 @@ Spring Boot application configured to be a task (annotated with the `@EnableTask annotation). At the beginning of a task, an entry in the `TaskRepository` is created recording the -start event. This event is triggered via the `ContextRefreshEvent` being triggered by -Spring Framework. +start event. This event is triggered via `SmartLifecycle#start` being triggered by +Spring Framework. This indicates to the system that all beans are ready for use and is +before the execution of any of the `*Runner`s provided by Spring Boot. -NOTE: As Spring Cloud Task is expected to consist of a single application context. If -multiple application contexts are used (parent/child relationships for example), the first -`ContextRefreshEvent` that is published by Spring will be recorded as the start of the -task. NOTE: The recording of a task will only occur upon the successful bootstrapping of an `ApplicationContext`. If the context fails to bootstrap at all, the task's execution will @@ -70,7 +67,7 @@ assumed to be 0. |The name for the task as determined by the configured `TaskNameResolver`. |`starTime` -|The time the task was started as indicated by the `ContextRefreshEvent`. +|The time the task was started as indicated by the `SmartLifecycle#start` call. |`endTime` |The time the task was completed as indicated by the `ContextClosedEvent`.