From 7a9a2a9c50c0e1f7b103dd8e81e283e3503a85bb Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Fri, 16 Feb 2018 18:10:26 +0100 Subject: [PATCH] BATCH-2537: add support for @Primary annotated data sources Currently, when multiple data sources are defined in the context, an IllegalStateException is thrown even if one of the data sources is annotated with @Primary (which should be the one to use). This commit makes it possible to use the data source annotated with @Primary when multiple data sources are defined. Note that the context initialization will still fail (with a UnsatisfiedDependencyException from Spring's bean factory) if multiple data sources are defined and none of them is annotated with @Primary. If multiple data sources are defined and none of them is annotated with @Primary but one of them is named "dataSource", this data source will be used by the batch configuration due to autowiring by name (this detail has been documented in the javadoc of @EnableBatchProcessing). Resolves BATCH-2537 --- .../AbstractBatchConfiguration.java | 15 +++---- .../annotation/EnableBatchProcessing.java | 11 ++++- .../MapJobRepositoryConfigurationTests.java | 44 +++++++++++++++++-- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java index ce714604b..92323de7f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/AbstractBatchConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-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. @@ -41,6 +41,7 @@ import java.util.Collection; * * @author Dave Syer * @author Michael Minella + * @author Mahmoud Ben Hassine * @since 2.2 * @see EnableBatchProcessing */ @@ -49,7 +50,7 @@ import java.util.Collection; public abstract class AbstractBatchConfiguration implements ImportAware { @Autowired(required = false) - private Collection dataSources; + private DataSource dataSource; private BatchConfigurer configurer; @@ -93,20 +94,16 @@ public abstract class AbstractBatchConfiguration implements ImportAware { return this.configurer; } if (configurers == null || configurers.isEmpty()) { - if (dataSources == null || dataSources.isEmpty()) { + if (dataSource == null) { DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(); configurer.initialize(); this.configurer = configurer; return configurer; - } else if(dataSources != null && dataSources.size() == 1) { - DataSource dataSource = dataSources.iterator().next(); + } else { DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(dataSource); configurer.initialize(); this.configurer = configurer; return configurer; - } else { - throw new IllegalStateException("To use the default BatchConfigurer the context must contain no more than " + - "one DataSource, found " + dataSources.size()); } } if (configurers.size() > 1) { @@ -144,4 +141,4 @@ class ScopeConfiguration { jobScope.setAutoProxy(false); return jobScope; } -} \ No newline at end of file +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java index da35e10d5..5fa35dfb2 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-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. @@ -27,6 +27,7 @@ import org.springframework.batch.core.configuration.support.ApplicationContextFa import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.repository.JobRepository; +import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.context.annotation.Import; import org.springframework.transaction.PlatformTransactionManager; @@ -85,7 +86,12 @@ import org.springframework.transaction.PlatformTransactionManager; * * * If a user does not provide a {@link javax.sql.DataSource} within the context, a Map based - * {@link org.springframework.batch.core.repository.JobRepository} will be used. + * {@link org.springframework.batch.core.repository.JobRepository} will be used. If multiple + * {@link javax.sql.DataSource}s are defined in the context, the one annotated with + * {@link org.springframework.context.annotation.Primary} will be used (Note that if none + * of them is annotated with {@link org.springframework.context.annotation.Primary}, the one + * named dataSource will be used if any, otherwise a {@link UnsatisfiedDependencyException} + * will be thrown). * * Note that only one of your configuration classes needs to have the @EnableBatchProcessing * annotation. Once you have an @EnableBatchProcessing class in your configuration you will have an @@ -156,6 +162,7 @@ import org.springframework.transaction.PlatformTransactionManager; * * * @author Dave Syer + * @author Mahmoud Ben Hassine * */ @Target(ElementType.TYPE) diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/MapJobRepositoryConfigurationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/MapJobRepositoryConfigurationTests.java index 7bff0fcbd..b45ac65b5 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/MapJobRepositoryConfigurationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/MapJobRepositoryConfigurationTests.java @@ -33,9 +33,11 @@ import org.springframework.batch.core.repository.JobRepository; 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.UnsatisfiedDependencyException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; import org.springframework.context.support.GenericApplicationContext; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.stereotype.Component; @@ -57,11 +59,21 @@ public class MapJobRepositoryConfigurationTests { testConfigurationClass(HsqlBatchConfiguration.class); } - @Test(expected = IllegalStateException.class) - public void testMultipleDataSources() throws Exception { + @Test(expected = UnsatisfiedDependencyException.class) + public void testMultipleDataSources_whenNoneOfThemIsPrimary() throws Exception { testConfigurationClass(InvalidBatchConfiguration.class); } + @Test + public void testMultipleDataSources_whenNoneOfThemIsPrimaryButOneOfThemIsNamed_dataSource_() throws Exception { + testConfigurationClass(ValidBatchConfigurationWithoutPrimaryDataSource.class); + } + + @Test + public void testMultipleDataSources_whenOneOfThemIsPrimary() throws Exception { + testConfigurationClass(ValidBatchConfigurationWithPrimaryDataSource.class); + } + private void testConfigurationClass(Class clazz) throws Exception { GenericApplicationContext context = new AnnotationConfigApplicationContext(clazz); this.jobLauncher = context.getBean(JobLauncher.class); @@ -85,11 +97,37 @@ public class MapJobRepositoryConfigurationTests { } } + public static class ValidBatchConfigurationWithPrimaryDataSource extends HsqlBatchConfiguration { + + @Primary + @Bean + DataSource dataSource2() { + return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder(). + setName("dataSource2"). + addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql"). + addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql"). + build()); + } + } + + public static class ValidBatchConfigurationWithoutPrimaryDataSource extends HsqlBatchConfiguration { + + @Bean + DataSource dataSource() { // will be autowired by name + return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder(). + setName("dataSource"). + addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql"). + addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql"). + build()); + } + } + public static class HsqlBatchConfiguration extends MapRepositoryBatchConfiguration { @Bean - DataSource dataSource() { + DataSource dataSource1() { return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder(). + setName("dataSource1"). addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql"). addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql"). build());