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 0967c2e8b..ad15f974f 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 @@ -15,10 +15,6 @@ */ package org.springframework.batch.core.configuration.annotation; -import java.util.Collection; - -import javax.sql.DataSource; - import org.springframework.batch.core.configuration.JobRegistry; import org.springframework.batch.core.configuration.support.MapJobRegistry; import org.springframework.batch.core.launch.JobLauncher; @@ -35,6 +31,9 @@ import org.springframework.core.type.AnnotationMetadata; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.util.Assert; +import javax.sql.DataSource; +import java.util.Collection; + /** * Base {@code Configuration} class providing common structure for enabling and using Spring Batch. Customization is * available by implementing the {@link BatchConfigurer} interface. {@link BatchConfigurer}. @@ -92,16 +91,21 @@ public abstract class AbstractBatchConfiguration implements ImportAware { return this.configurer; } if (configurers == null || configurers.isEmpty()) { - if (dataSources == null || dataSources.isEmpty() || dataSources.size() > 1) { - throw new IllegalStateException( - "To use the default BatchConfigurer the context must contain precisely one DataSource, found " - + (dataSources == null ? 0 : dataSources.size())); + if (dataSources == null || dataSources.isEmpty()) { + DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(); + configurer.initialize(); + this.configurer = configurer; + return configurer; + } else if(dataSources != null && dataSources.size() == 1) { + DataSource dataSource = dataSources.iterator().next(); + 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()); } - DataSource dataSource = dataSources.iterator().next(); - DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(dataSource); - configurer.initialize(); - this.configurer = configurer; - return configurer; } if (configurers.size() > 1) { throw new IllegalStateException( diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/DefaultBatchConfigurer.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/DefaultBatchConfigurer.java index 7a672a491..5564bbd86 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/DefaultBatchConfigurer.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/DefaultBatchConfigurer.java @@ -15,27 +15,32 @@ */ package org.springframework.batch.core.configuration.annotation; -import javax.annotation.PostConstruct; -import javax.sql.DataSource; - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.support.SimpleJobLauncher; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; +import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean; +import org.springframework.batch.support.transaction.ResourcelessTransactionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + @Component public class DefaultBatchConfigurer implements BatchConfigurer { + private static final Log logger = LogFactory.getLog(DefaultBatchConfigurer.class); private DataSource dataSource; private PlatformTransactionManager transactionManager; private JobRepository jobRepository; private JobLauncher jobLauncher; - @Autowired + @Autowired(required = false) public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.transactionManager = new DataSourceTransactionManager(dataSource); @@ -64,7 +69,20 @@ public class DefaultBatchConfigurer implements BatchConfigurer { @PostConstruct public void initialize() throws Exception { - this.jobRepository = createJobRepository(); + if(dataSource == null) { + logger.warn("No datasource was provided...using a Map based JobRepository"); + + if(this.transactionManager == null) { + this.transactionManager = new ResourcelessTransactionManager(); + } + + MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean(this.transactionManager); + factory.afterPropertiesSet(); + this.jobRepository = factory.getObject(); + } else { + this.jobRepository = createJobRepository(); + } + this.jobLauncher = createJobLauncher(); } @@ -82,5 +100,4 @@ public class DefaultBatchConfigurer implements BatchConfigurer { factory.afterPropertiesSet(); return factory.getObject(); } - } 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 bb13f561f..662197c0c 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-2013 the original author or authors. + * Copyright 2012-2014 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. @@ -15,14 +15,6 @@ */ package org.springframework.batch.core.configuration.annotation; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import javax.sql.DataSource; - import org.springframework.batch.core.configuration.JobRegistry; import org.springframework.batch.core.configuration.support.ApplicationContextFactory; import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar; @@ -31,6 +23,13 @@ import org.springframework.batch.core.repository.JobRepository; import org.springframework.context.annotation.Import; import org.springframework.transaction.PlatformTransactionManager; +import javax.sql.DataSource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** *
* Enable Spring Batch features and provide a base configuration for setting up batch jobs in an @Configuration @@ -62,7 +61,7 @@ import org.springframework.transaction.PlatformTransactionManager; * } * * - * The user has to provide a {@link DataSource} as a bean in the context, or else implement {@link BatchConfigurer} in + * The user should to provide a {@link DataSource} as a bean in the context, or else implement {@link BatchConfigurer} in * the configuration class itself, e.g. * *
@@ -85,6 +84,9 @@ 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. + * * 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
* instance of {@link StepScope} so your beans inside steps can have @Scope("step"). You will also be
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
new file mode 100644
index 000000000..e82a4f59e
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/MapJobRepositoryConfigurationTests.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2014 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.batch.core.configuration.annotation;
+
+import org.junit.Test;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.PooledEmbeddedDataSource;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.StepContribution;
+import org.springframework.batch.core.launch.JobLauncher;
+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.annotation.Autowired;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.stereotype.Component;
+
+import javax.sql.DataSource;
+
+import static junit.framework.Assert.assertEquals;
+
+public class MapJobRepositoryConfigurationTests {
+
+ JobLauncher jobLauncher;
+ JobRepository jobRepository;
+ Job job;
+
+ @Test
+ public void testRoseyScenario() throws Exception {
+ testConfigurationClass(MapRepositoryBatchConfiguration.class);
+ }
+
+ @Test
+ public void testOneDataSource() throws Exception {
+ testConfigurationClass(HsqlBatchConfiguration.class);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testMultipleDataSources() throws Exception {
+ testConfigurationClass(InvalidBatchConfiguration.class);
+ }
+
+ private void testConfigurationClass(Class clazz) throws Exception {
+ GenericApplicationContext context = new AnnotationConfigApplicationContext(clazz);
+ this.jobLauncher = context.getBean(JobLauncher.class);
+ this.jobRepository = context.getBean(JobRepository.class);
+ this.job = context.getBean(Job.class);
+
+ JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
+ assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
+ JobExecution repositoryJobExecution = jobRepository.getLastJobExecution(job.getName(), new JobParameters());
+ assertEquals(jobExecution.getId(), repositoryJobExecution.getId());
+ context.close();
+ }
+
+ public static class InvalidBatchConfiguration extends HsqlBatchConfiguration {
+
+ @Bean
+ DataSource dataSource2() {
+ return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder().setName("badDatabase").build());
+ }
+ }
+
+ public static class HsqlBatchConfiguration extends MapRepositoryBatchConfiguration {
+
+ @Bean
+ DataSource dataSource() {
+ return new PooledEmbeddedDataSource(new EmbeddedDatabaseBuilder().
+ addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql").
+ addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql").
+ build());
+ }
+ }
+
+ @Component
+ @EnableBatchProcessing
+ public static class MapRepositoryBatchConfiguration {
+ @Autowired
+ JobBuilderFactory jobFactory;
+
+ @Autowired
+ StepBuilderFactory stepFactory;
+
+ @Bean
+ Step step1 () {
+ return stepFactory.get("step1").tasklet(new Tasklet() {
+ @Override
+ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
+ return RepeatStatus.FINISHED;
+ }
+ }).build();
+ }
+
+ @Bean
+ Job job() {
+ return jobFactory.get("job").start(step1()).build();
+ }
+ }
+}