BATCH-2048: Simplified the use fo a MapJobRepository by making the DataSource optional

This commit is contained in:
Michael Minella
2014-03-21 11:41:00 -05:00
parent fec3d57f22
commit de7df9c1f1
4 changed files with 171 additions and 29 deletions

View File

@@ -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(

View File

@@ -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();
}
}

View File

@@ -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;
/**
* <p>
* Enable Spring Batch features and provide a base configuration for setting up batch jobs in an &#064;Configuration
@@ -62,7 +61,7 @@ import org.springframework.transaction.PlatformTransactionManager;
* }
* </pre>
*
* 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.
*
* <pre class="code">
@@ -85,6 +84,9 @@ import org.springframework.transaction.PlatformTransactionManager;
* }
* </pre>
*
* 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 <code>&#064;EnableBatchProcessing</code>
* annotation. Once you have an <code>&#064;EnableBatchProcessing</code> class in your configuration you will have an
* instance of {@link StepScope} so your beans inside steps can have <code>&#064;Scope("step")</code>. You will also be

View File

@@ -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();
}
}
}