BATCH-1918: First effort at @EnableBatchProcessing
Example:
@Configuration
@EnableBatchProcessing
public class RetrySampleConfiguration {
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Bean
public Job retrySample() throws Exception {
return jobs.get("retrySample").start(step()).build();
}
@Bean
protected Step step() throws Exception {
return steps.get("step").<Trade, Object> chunk(1).reader(reader()).writer(writer()).faultTolerant()
.retry(Exception.class).retryLimit(3).build();
}
@Bean
protected ItemReader<Trade> reader() {
GeneratingTradeItemReader reader = new GeneratingTradeItemReader();
reader.setLimit(10);
return reader;
}
@Bean
protected ItemWriter<Object> writer() {
return new RetrySampleItemWriter<Object>();
}
}
This commit is contained in:
committed by
Michael Minella
parent
96de2dc4e9
commit
25cd6d6826
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 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;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.scope.StepScope;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base {@code Configuration} class providing common structure for enabling and using Spring Batch. Customization is
|
||||
* available by implementing the {@link BatchConfigurer} interface.
|
||||
* {@link BatchConfigurer}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.2
|
||||
* @see EnableBatchProcessing
|
||||
*/
|
||||
@Configuration
|
||||
@Import(StepScopeConfiguration.class)
|
||||
public abstract class AbstractBatchConfiguration implements ImportAware {
|
||||
|
||||
@Autowired
|
||||
private StepScope stepScope;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Collection<DataSource> dataSources;
|
||||
|
||||
private BatchConfigurer configurer;
|
||||
|
||||
@Bean
|
||||
public JobBuilderFactory jobBuilders() throws Exception {
|
||||
return new JobBuilderFactory(jobRepository());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StepBuilderFactory stepBuilders() throws Exception {
|
||||
return new StepBuilderFactory(jobRepository(), transactionManager());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public abstract JobRepository jobRepository() throws Exception;
|
||||
|
||||
@Bean
|
||||
public abstract JobLauncher jobLauncher() throws Exception;
|
||||
|
||||
@Bean
|
||||
public JobRegistry jobRegistry() throws Exception {
|
||||
return new MapJobRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public abstract PlatformTransactionManager transactionManager() throws Exception;
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
AnnotationAttributes enabled = AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(
|
||||
EnableBatchProcessing.class.getName(), false));
|
||||
Assert.notNull(enabled,
|
||||
"@EnableBatchProcessing is not present on importing class " + importMetadata.getClassName());
|
||||
if (enabled.getBoolean("proxyTargetClass")) {
|
||||
stepScope.setProxyTargetClass(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected BatchConfigurer getConfigurer(Collection<BatchConfigurer> configurers) throws Exception {
|
||||
if (this.configurer!=null) {
|
||||
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()));
|
||||
}
|
||||
DataSource dataSource = dataSources.iterator().next();
|
||||
DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(dataSource);
|
||||
configurer.initialize();
|
||||
this.configurer = configurer;
|
||||
return configurer;
|
||||
}
|
||||
if (configurers.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"To use a custom BatchConfigurer the context must contain precisely one, found "
|
||||
+ configurers.size());
|
||||
}
|
||||
this.configurer = configurers.iterator().next();
|
||||
return this.configurer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract step scope configuration into a separate unit so that it can be non-static.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
class StepScopeConfiguration {
|
||||
|
||||
private StepScope stepScope = new StepScope();
|
||||
|
||||
@Bean
|
||||
public StepScope stepScope() {
|
||||
return stepScope;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base {@code Configuration} class providing common structure for enabling and using Spring Batch. Customization is
|
||||
* available by implementing the {@link BatchConfigurer} interface.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.2
|
||||
* @see EnableBatchProcessing
|
||||
*/
|
||||
public class BatchConfigurationSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
Class<?> annotationType = EnableBatchProcessing.class;
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(
|
||||
annotationType.getName(), false));
|
||||
Assert.notNull(attributes, String.format("@%s is not present on importing class '%s' as expected",
|
||||
annotationType.getSimpleName(), importingClassMetadata.getClassName()));
|
||||
|
||||
String[] imports;
|
||||
if (attributes.containsKey("modular") && attributes.getBoolean("modular")) {
|
||||
imports = new String[] { ModularBatchConfiguration.class.getName() };
|
||||
}
|
||||
else {
|
||||
imports = new String[] { SimpleBatchConfiguration.class.getName() };
|
||||
}
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Strategy interface for users to provide as a factory for custom components needed by a Batch system.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface BatchConfigurer {
|
||||
|
||||
JobRepository getJobRepository() throws Exception;
|
||||
|
||||
PlatformTransactionManager getTransactionManager() throws Exception;
|
||||
|
||||
JobLauncher getJobLauncher() throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
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.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@Component
|
||||
public class DefaultBatchConfigurer implements BatchConfigurer {
|
||||
|
||||
private DataSource dataSource;
|
||||
private PlatformTransactionManager transactionManager;
|
||||
private JobRepository jobRepository;
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
this.transactionManager = new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
|
||||
protected DefaultBatchConfigurer() {}
|
||||
|
||||
public DefaultBatchConfigurer(DataSource dataSource) {
|
||||
setDataSource(dataSource);
|
||||
}
|
||||
|
||||
public JobRepository getJobRepository() {
|
||||
return jobRepository;
|
||||
}
|
||||
|
||||
public PlatformTransactionManager getTransactionManager() {
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
public JobLauncher getJobLauncher() {
|
||||
return jobLauncher;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() throws Exception {
|
||||
this.jobRepository = createJobRepository();
|
||||
this.jobLauncher = createJobLauncher();
|
||||
}
|
||||
|
||||
private JobLauncher createJobLauncher() throws Exception {
|
||||
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
|
||||
jobLauncher.setJobRepository(jobRepository);
|
||||
jobLauncher.afterPropertiesSet();
|
||||
return jobLauncher;
|
||||
}
|
||||
|
||||
protected JobRepository createJobRepository() throws Exception {
|
||||
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
|
||||
factory.setDataSource(dataSource);
|
||||
factory.setTransactionManager(transactionManager);
|
||||
factory.afterPropertiesSet();
|
||||
return (JobRepository) factory.getObject();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 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.support.ApplicationContextFactory;
|
||||
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.batch.core.scope.StepScope;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Enable Spring Batch features and provide a base configuration for setting up batch jobs in an @Configuration
|
||||
* class, roughly equivalent to using the {@code <batch:*>} XML namespace.
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableBatchProcessing
|
||||
* @Import(DataSourceCnfiguration.class)
|
||||
* public class AppConfig {
|
||||
*
|
||||
* @Autowired
|
||||
* private JobBuilderFactory jobs;
|
||||
*
|
||||
* @Bean
|
||||
* public Job job() {
|
||||
* return jobs.get("myJob").start(step1()).next(step2()).build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* protected Step step1() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* protected Step step2() {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* The user has 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">
|
||||
* @Configuration
|
||||
* @EnableBatchProcessing
|
||||
* public class AppConfig extends DefaultBatchConfigurer {
|
||||
*
|
||||
* @Bean
|
||||
* public Job job() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected JobRepository createJobRepository() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Note that only one of your configuration classes needs to have the <code>@EnableBatchProcessing</code>
|
||||
* annotation. Once you have an <code>@EnableBatchProcessing</code> class in your configuration you will have an
|
||||
* instance of {@link StepScope} so your beans inside steps can have <code>@Scope("step")</code>. You will also be
|
||||
* able to <code>@Autowired</code> some useful stuff into your context:
|
||||
*
|
||||
* <ul>
|
||||
* <li>a {@link JobRepository} (bean name "jobRepository")</li>
|
||||
* <li>a {@link JobLauncher} (bean name "jobLauncher")</li>
|
||||
* <li>a {@link JobRegistry} (bean name "jobRegistry")</li>
|
||||
* <li>a {@link PlatformTransactionManager} (bean name "transactionManager")</li>
|
||||
* <li>a {@link JobBuilderFactory} (bean name "jobBuilders") as a convenience to prevent you from having to inject the
|
||||
* job repository into every job, as in the examples above</li>
|
||||
* <li>a {@link StepBuilderFactory} (bean name "stepBuilders") as a convenience to prevent you from having to inject the
|
||||
* job repository and transaction manager into every step</li>
|
||||
* </ul>
|
||||
*
|
||||
* If the configuration is specified as <code>modular=true</code> then the context will also contain an
|
||||
* {@link AutomaticJobRegistrar}. The job registrar is useful for modularizing your configuration if there are multiple
|
||||
* jobs. It works by creating separate child application contexts containing job configurations and registering those
|
||||
* jobs. The jobs can then create steps and other dependent components without needing to worry about bean definition
|
||||
* name clashes. Beans of type {@link ApplicationContextFactory} will be registered automatically with the job
|
||||
* registrar. Example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableBatchProcessing(modular=true)
|
||||
* public class AppConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public ApplicationContextFactory someJobs() {
|
||||
* return new GenericApplicationContextFactory(SomeJobConfiguration.class);
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public ApplicationContextFactory moreJobs() {
|
||||
* return new GenericApplicationContextFactory(MoreJobConfiguration.class);
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Note that a modular parent context in general should <em>not</em> itself contain @Bean definitions for job,
|
||||
* especially if a {@link BatchConfigurer} is provided, because cyclic configuration dependencies are otherwise likely
|
||||
* to develop.
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For reference, the first example above can be compared to the following Spring XML configuration:
|
||||
*
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* <batch>
|
||||
* <job-repository />
|
||||
* <job id="myJob">
|
||||
* <step id="step1" .../>
|
||||
* <step id="step2" .../>
|
||||
* </job>
|
||||
* <beans:bean id="transactionManager" .../>
|
||||
* <beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
* <beans:property name="jobRepository" ref="jobRepository" />
|
||||
* </beans:bean>
|
||||
* </batch>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(BatchConfigurationSelector.class)
|
||||
public @interface EnableBatchProcessing {
|
||||
|
||||
/**
|
||||
* Indicate whether beans in <code>scope="step"</code> should use subclass-based (CGLIB) proxies are to be created
|
||||
* as opposed to standard Java interface-based proxies. The default is {@code false}.
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
/**
|
||||
* Indicate whether the configuration is going to be modularized into multiple application contexts. If true then
|
||||
* you should not create any @Bean Job definitions in this context, but rather supply them in separate (child)
|
||||
* contexts through an {@link ApplicationContextFactory}.
|
||||
*/
|
||||
boolean modular() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.batch.core.job.builder.JobBuilder;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
|
||||
/**
|
||||
* Convenient factory for a {@link JobBuilder} which sets the {@link JobRepository} automatically.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JobBuilderFactory {
|
||||
|
||||
private JobRepository jobRepository;
|
||||
|
||||
public JobBuilderFactory(JobRepository jobRepository) {
|
||||
this.jobRepository = jobRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a job builder and initializes its job repository. Note that if the builder is used to create a @Bean
|
||||
* definition then the name of the job and the bean name might be different.
|
||||
*
|
||||
* @param name the name of the job
|
||||
* @return a job builder
|
||||
*/
|
||||
public JobBuilder get(String name) {
|
||||
JobBuilder builder = new JobBuilder(name).repository(jobRepository);
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.configuration.support.ApplicationContextFactory;
|
||||
import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar;
|
||||
import org.springframework.batch.core.configuration.support.DefaultJobLoader;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Base {@code Configuration} class providing common structure for enabling and using Spring Batch. Customization is
|
||||
* available by implementing the {@link BatchConfigurer} interface.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.2
|
||||
* @see EnableBatchProcessing
|
||||
*/
|
||||
@Configuration
|
||||
public class ModularBatchConfiguration extends AbstractBatchConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Collection<BatchConfigurer> configurers;
|
||||
|
||||
private AutomaticJobRegistrar registrar = new AutomaticJobRegistrar();
|
||||
|
||||
@Bean
|
||||
public JobRepository jobRepository() throws Exception {
|
||||
return getConfigurer(configurers).getJobRepository();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobLauncher jobLauncher() throws Exception {
|
||||
return getConfigurer(configurers).getJobLauncher();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() throws Exception {
|
||||
return getConfigurer(configurers).getTransactionManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AutomaticJobRegistrar jobRegistrar() throws Exception {
|
||||
registrar.setJobLoader(new DefaultJobLoader(jobRegistry()));
|
||||
for (ApplicationContextFactory factory : context.getBeansOfType(ApplicationContextFactory.class).values()) {
|
||||
registrar.addApplicationContextFactory(factory);
|
||||
}
|
||||
return registrar;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.AbstractLazyCreationTargetSource;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.support.MapJobRegistry;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Base {@code Configuration} class providing common structure for enabling and using Spring Batch. Customization is
|
||||
* available by implementing the {@link BatchConfigurer} interface. The main components are created as lazy proxies that
|
||||
* only initialize when a method is called. This is to prevent (as much as possible) configuration cycles from
|
||||
* developing when these components are needed in a configuration resource that itself provides a
|
||||
* {@link BatchConfigurer}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 2.2
|
||||
* @see EnableBatchProcessing
|
||||
*/
|
||||
@Configuration
|
||||
public class SimpleBatchConfiguration extends AbstractBatchConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
private AtomicReference<JobRepository> jobRepository = new AtomicReference<JobRepository>();
|
||||
|
||||
private AtomicReference<JobLauncher> jobLauncher = new AtomicReference<JobLauncher>();
|
||||
|
||||
private AtomicReference<JobRegistry> jobRegistry = new AtomicReference<JobRegistry>();
|
||||
|
||||
private AtomicReference<PlatformTransactionManager> transactionManager = new AtomicReference<PlatformTransactionManager>();
|
||||
|
||||
@Bean
|
||||
public JobRepository jobRepository() throws Exception {
|
||||
return createLazyProxy(jobRepository, JobRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobLauncher jobLauncher() throws Exception {
|
||||
return createLazyProxy(jobLauncher, JobLauncher.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobRegistry jobRegistry() throws Exception {
|
||||
return createLazyProxy(jobRegistry, JobRegistry.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() throws Exception {
|
||||
return createLazyProxy(transactionManager, PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
private <T> T createLazyProxy(AtomicReference<T> reference, Class<T> type) {
|
||||
ProxyFactory factory = new ProxyFactory();
|
||||
factory.setTargetSource(new ReferenceTargetSource<T>(reference));
|
||||
factory.addAdvice(new PassthruAdvice());
|
||||
factory.setInterfaces(new Class<?>[] { type });
|
||||
@SuppressWarnings("unchecked")
|
||||
T proxy = (T) factory.getProxy();
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the basic components by extracting them from the {@link BatchConfigurer configurer}, defaulting to some
|
||||
* sensible values as long as a unique DataSource is available.
|
||||
*
|
||||
* @throws Exception if there is a problem in the configurer
|
||||
*/
|
||||
protected void initialize() throws Exception {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
BatchConfigurer configurer = getConfigurer(context.getBeansOfType(BatchConfigurer.class).values());
|
||||
jobRepository.set(configurer.getJobRepository());
|
||||
jobLauncher.set(configurer.getJobLauncher());
|
||||
transactionManager.set(configurer.getTransactionManager());
|
||||
jobRegistry.set(new MapJobRegistry());
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private class PassthruAdvice implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ReferenceTargetSource<T> extends AbstractLazyCreationTargetSource {
|
||||
|
||||
private AtomicReference<T> reference;
|
||||
|
||||
public ReferenceTargetSource(AtomicReference<T> reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createObject() throws Exception {
|
||||
initialize();
|
||||
return reference.get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Convenient factory for a {@link StepBuilder} which sets the {@link JobRepository} and
|
||||
* {@link PlatformTransactionManager} automatically.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepBuilderFactory {
|
||||
|
||||
private JobRepository jobRepository;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
public StepBuilderFactory(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a step builder and initializes its job repository and transaction manager. Note that if the builder is
|
||||
* used to create a @Bean definition then the name of the step and the bean name might be different.
|
||||
*
|
||||
* @param name the name of the step
|
||||
* @return a step builder
|
||||
*/
|
||||
public StepBuilder get(String name) {
|
||||
StepBuilder builder = (StepBuilder) new StepBuilder(name).repository(jobRepository).transactionManager(
|
||||
transactionManager);
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.CustomEditorConfigurer;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.beans.factory.support.AbstractBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create.
|
||||
* When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child
|
||||
* context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that
|
||||
* it is closed when it is no longer needed. If a path is not set, the parent will always be returned.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractApplicationContextFactory.class);
|
||||
|
||||
private Object resource;
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
|
||||
private boolean copyConfiguration = true;
|
||||
|
||||
private Collection<Class<? extends BeanFactoryPostProcessor>> beanFactoryPostProcessorClasses;
|
||||
|
||||
private Collection<Class<?>> beanPostProcessorExcludeClasses;
|
||||
|
||||
/**
|
||||
* Create a factory instance with the resource specified. The resource is a Spring configuration file or java
|
||||
* package containing configuration files.
|
||||
*/
|
||||
public AbstractApplicationContextFactory(Object resource) {
|
||||
|
||||
this.resource = resource;
|
||||
beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
|
||||
beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class);
|
||||
beanFactoryPostProcessorClasses.add(CustomEditorConfigurer.class);
|
||||
beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
|
||||
/*
|
||||
* Assume that a BeanPostProcessor that is BeanFactoryAware must be specific to the parent and remove it from
|
||||
* the child (e.g. an AutoProxyCreator will not work properly). Unfortunately there might still be a a
|
||||
* BeanPostProcessor with a dependency that itself is BeanFactoryAware, but we can't legislate for that here.
|
||||
*/
|
||||
beanPostProcessorExcludeClasses.add(BeanFactoryAware.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that configuration such as bean post processors and custom editors should be copied from the
|
||||
* parent context. Defaults to true.
|
||||
*
|
||||
* @param copyConfiguration the flag value to set
|
||||
*/
|
||||
public void setCopyConfiguration(boolean copyConfiguration) {
|
||||
this.copyConfiguration = copyConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected access for subclasses to the flag determining whether configuration should be copied from parent
|
||||
* context.
|
||||
*
|
||||
* @return the flag value
|
||||
*/
|
||||
protected final boolean isCopyConfiguration() {
|
||||
return copyConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which bean factory post processors (like property placeholders) should be copied from the parent
|
||||
* context. Defaults to {@link PropertyPlaceholderConfigurer} and {@link CustomEditorConfigurer}.
|
||||
*
|
||||
* @param copyBeanFactoryPostProcessors the flag value to set
|
||||
*/
|
||||
|
||||
public void setBeanFactoryPostProcessorClasses(
|
||||
Class<? extends BeanFactoryPostProcessor>[] beanFactoryPostProcessorClasses) {
|
||||
this.beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
|
||||
for (int i = 0; i < beanFactoryPostProcessorClasses.length; i++) {
|
||||
this.beanFactoryPostProcessorClasses.add(beanFactoryPostProcessorClasses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines by exclusion which bean post processors should be copied from the parent context. Defaults to
|
||||
* {@link BeanFactoryAware} (so any post processors that have a reference to the parent bean factory are not copied
|
||||
* into the child). Note that these classes do not themselves have to be {@link BeanPostProcessor} implementations
|
||||
* or sub-interfaces.
|
||||
*
|
||||
* @param beanPostProcessorExcludeClasses the classes to set
|
||||
*/
|
||||
public void setBeanPostProcessorExcludeClasses(Class<?>[] beanPostProcessorExcludeClasses) {
|
||||
this.beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
|
||||
for (int i = 0; i < beanPostProcessorExcludeClasses.length; i++) {
|
||||
this.beanPostProcessorExcludeClasses.add(beanPostProcessorExcludeClasses[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected access to the list of bean factory post processor classes that should be copied over to the context
|
||||
* from the parent.
|
||||
*
|
||||
* @return the classes for post processors that were nominated for copying
|
||||
*/
|
||||
protected final Collection<Class<? extends BeanFactoryPostProcessor>> getBeanFactoryPostProcessorClasses() {
|
||||
return beanFactoryPostProcessorClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent application context.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
if (applicationContext == null) {
|
||||
return;
|
||||
}
|
||||
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
|
||||
parent = (ConfigurableApplicationContext) applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ApplicationContext} from the provided path.
|
||||
*
|
||||
* @see ApplicationContextFactory#createApplicationContext()
|
||||
*/
|
||||
public ConfigurableApplicationContext createApplicationContext() {
|
||||
|
||||
if (resource == null) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
return createApplicationContext(parent, resource);
|
||||
|
||||
}
|
||||
|
||||
protected abstract ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
|
||||
Object resource);
|
||||
|
||||
/**
|
||||
* Extension point for special subclasses that want to do more complex things with the context prior to refresh. The
|
||||
* default implementation does nothing.
|
||||
*
|
||||
* @param parent the parent for the new application context
|
||||
* @param context the new application context before it is refreshed, but after bean factory is initialized
|
||||
*
|
||||
* @see AbstractApplicationContextFactory#setBeanFactoryPostProcessorClasses(Class[])
|
||||
*/
|
||||
protected void prepareContext(ConfigurableApplicationContext parent, ConfigurableApplicationContext context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point for special subclasses that want to do more complex things with the bean factory prior to
|
||||
* refresh. The default implementation copies all configuration from the parent according to the
|
||||
* {@link #setCopyConfiguration(boolean) flag} set.
|
||||
*
|
||||
* @param parent the parent bean factory for the new context (will never be null)
|
||||
* @param beanFactory the new bean factory before bean definitions are loaded
|
||||
*
|
||||
* @see AbstractApplicationContextFactory#setCopyConfiguration(boolean)
|
||||
* @see DefaultListableBeanFactory#copyConfigurationFrom(ConfigurableBeanFactory)
|
||||
*/
|
||||
protected void prepareBeanFactory(ConfigurableListableBeanFactory parent,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
if (copyConfiguration && parent != null) {
|
||||
beanFactory.copyConfigurationFrom(parent);
|
||||
List<BeanPostProcessor> beanPostProcessors = beanFactory instanceof AbstractBeanFactory ? ((AbstractBeanFactory) beanFactory)
|
||||
.getBeanPostProcessors() : new ArrayList<BeanPostProcessor>();
|
||||
for (BeanPostProcessor beanPostProcessor : new ArrayList<BeanPostProcessor>(beanPostProcessors)) {
|
||||
for (Class<?> cls : beanPostProcessorExcludeClasses) {
|
||||
if (cls.isAssignableFrom(beanPostProcessor.getClass())) {
|
||||
logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls);
|
||||
beanPostProcessors.remove(beanPostProcessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApplicationContextFactory [resource=" + resource + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
return toString().equals(obj.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,18 +34,17 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Loads and unloads {@link Job Jobs} when the application context is created
|
||||
* and destroyed. Each resource provided is loaded as an application context
|
||||
* with the current context as its parent, and then all the jobs from the child
|
||||
* context are registered under their bean names. A {@link JobRegistry} is
|
||||
* required.
|
||||
* Loads and unloads {@link Job Jobs} when the application context is created and destroyed. Each resource provided is
|
||||
* loaded as an application context with the current context as its parent, and then all the jobs from the child context
|
||||
* are registered under their bean names. A {@link JobRegistry} is required.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationListener, ApplicationContextAware, InitializingBean {
|
||||
public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationListener, ApplicationContextAware,
|
||||
InitializingBean {
|
||||
|
||||
private Collection<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
|
||||
|
||||
@@ -56,15 +55,14 @@ public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationLis
|
||||
private volatile boolean running = false;
|
||||
|
||||
private Object lifecycleMonitor = new Object();
|
||||
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* The enclosing application context, which can be used to check if
|
||||
* {@link ApplicationEvent events} come from the expected source.
|
||||
* The enclosing application context, which can be used to check if {@link ApplicationEvent events} come from the
|
||||
* expected source.
|
||||
*
|
||||
* @param applicationContext the enclosing application context if there is
|
||||
* one
|
||||
* @param applicationContext the enclosing application context if there is one
|
||||
* @see ApplicationContextAware#setApplicationContext(ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
@@ -74,8 +72,19 @@ public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationLis
|
||||
/**
|
||||
* Add some factories to the set that will be used to load contexts and jobs.
|
||||
*
|
||||
* @param applicationContextFactories the {@link ApplicationContextFactory}
|
||||
* values to use
|
||||
* @param applicationContextFactories the {@link ApplicationContextFactory} values to use
|
||||
*/
|
||||
public void addApplicationContextFactory(ApplicationContextFactory applicationContextFactory) {
|
||||
if (applicationContextFactory instanceof ApplicationContextAware) {
|
||||
((ApplicationContextAware) applicationContextFactory).setApplicationContext(applicationContext);
|
||||
}
|
||||
this.applicationContextFactories.add(applicationContextFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some factories to the set that will be used to load contexts and jobs.
|
||||
*
|
||||
* @param applicationContextFactories the {@link ApplicationContextFactory} values to use
|
||||
*/
|
||||
public void setApplicationContextFactories(ApplicationContextFactory[] applicationContextFactories) {
|
||||
for (ApplicationContextFactory applicationContextFactory : applicationContextFactories) {
|
||||
@@ -115,9 +124,8 @@ public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all the application contexts required and set up job registry
|
||||
* entries with all the instances of {@link Job} found therein. Also closes
|
||||
* the contexts when the enclosing context is closed.
|
||||
* Creates all the application contexts required and set up job registry entries with all the instances of
|
||||
* {@link Job} found therein. Also closes the contexts when the enclosing context is closed.
|
||||
*
|
||||
* @see InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@@ -146,8 +154,7 @@ public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Take all the contexts from the factories provided and pass them to teh
|
||||
* {@link JobLoader}.
|
||||
* Take all the contexts from the factories provided and pass them to the {@link JobLoader}.
|
||||
*
|
||||
* @see Lifecycle#start()
|
||||
*/
|
||||
|
||||
@@ -16,27 +16,8 @@
|
||||
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.CustomEditorConfigurer;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.AbstractXmlApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextFactory} implementation that takes a parent context
|
||||
@@ -46,279 +27,17 @@ import org.springframework.util.Assert;
|
||||
* initialized and cached. Clients should ensure that it is closed when it is no
|
||||
* longer needed. If a path is not set, the parent will always be returned.
|
||||
*
|
||||
* @deprecated use {@link GenericApplicationContextFactory} instead
|
||||
*/
|
||||
public class ClassPathXmlApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ClassPathXmlApplicationContextFactory.class);
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
|
||||
private boolean copyConfiguration = true;
|
||||
|
||||
private Collection<Class<? extends BeanFactoryPostProcessor>> beanFactoryPostProcessorClasses;
|
||||
|
||||
private Collection<Class<?>> beanPostProcessorExcludeClasses;
|
||||
public class ClassPathXmlApplicationContextFactory extends GenericApplicationContextFactory {
|
||||
|
||||
/**
|
||||
* Convenient constructor for configuration purposes.
|
||||
*/
|
||||
public ClassPathXmlApplicationContextFactory() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a factory instance with the resource specified. The resource is a
|
||||
* Spring XML configuration file.
|
||||
* Create an application context factory for the resource specified.
|
||||
*
|
||||
* @param resource a resource (XML configuration file)
|
||||
*/
|
||||
public ClassPathXmlApplicationContextFactory(Resource resource) {
|
||||
|
||||
this.resource = resource;
|
||||
beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
|
||||
beanFactoryPostProcessorClasses.add(PropertyPlaceholderConfigurer.class);
|
||||
beanFactoryPostProcessorClasses.add(CustomEditorConfigurer.class);
|
||||
beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
|
||||
/*
|
||||
* Assume that a BeanPostProcessor that is BeanFactoryAware must be
|
||||
* specific to the parent and remove it from the child (e.g. an
|
||||
* AutoProxyCreator will not work properly). Unfortunately there might
|
||||
* still be a a BeanPostProcessor with a dependency that itself is
|
||||
* BeanFactoryAware, but we can't legislate for that here.
|
||||
*/
|
||||
beanPostProcessorExcludeClasses.add(BeanFactoryAware.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the path to the xml to load to create an
|
||||
* {@link ApplicationContext}. Use imports to centralise the configuration
|
||||
* in one file.
|
||||
*
|
||||
* @param resource the resource path to the xml to load for the child
|
||||
* context.
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that configuration such as bean post processors and
|
||||
* custom editors should be copied from the parent context. Defaults to
|
||||
* true.
|
||||
*
|
||||
* @param copyConfiguration the flag value to set
|
||||
*/
|
||||
public void setCopyConfiguration(boolean copyConfiguration) {
|
||||
this.copyConfiguration = copyConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected access for subclasses to the flag determining whether
|
||||
* configuration should be copied from parent context.
|
||||
*
|
||||
* @return the flag value
|
||||
*/
|
||||
protected final boolean isCopyConfiguration() {
|
||||
return copyConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which bean factory post processors (like property
|
||||
* placeholders) should be copied from the parent context. Defaults to
|
||||
* {@link PropertyPlaceholderConfigurer} and {@link CustomEditorConfigurer}.
|
||||
*
|
||||
* @param copyBeanFactoryPostProcessors the flag value to set
|
||||
*/
|
||||
|
||||
public void setBeanFactoryPostProcessorClasses(
|
||||
Class<? extends BeanFactoryPostProcessor>[] beanFactoryPostProcessorClasses) {
|
||||
this.beanFactoryPostProcessorClasses = new ArrayList<Class<? extends BeanFactoryPostProcessor>>();
|
||||
for (int i = 0; i < beanFactoryPostProcessorClasses.length; i++) {
|
||||
this.beanFactoryPostProcessorClasses.add(beanFactoryPostProcessorClasses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines by exclusion which bean post processors should be copied from
|
||||
* the parent context. Defaults to {@link BeanFactoryAware} (so any post
|
||||
* processors that have a reference to the parent bean factory are not
|
||||
* copied into the child). Note that these classes do not themselves have to
|
||||
* be {@link BeanPostProcessor} implementations or sub-interfaces.
|
||||
*
|
||||
* @param beanPostProcessorExcludeClasses the classes to set
|
||||
*/
|
||||
public void setBeanPostProcessorExcludeClasses(Class<?>[] beanPostProcessorExcludeClasses) {
|
||||
this.beanPostProcessorExcludeClasses = new ArrayList<Class<?>>();
|
||||
for (int i = 0; i < beanPostProcessorExcludeClasses.length; i++) {
|
||||
this.beanPostProcessorExcludeClasses.add(beanPostProcessorExcludeClasses[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected access to the list of bean factory post processor classes that
|
||||
* should be copied over to the context from the parent.
|
||||
*
|
||||
* @return the classes for post processors that were nominated for copying
|
||||
*/
|
||||
protected final Collection<Class<? extends BeanFactoryPostProcessor>> getBeanFactoryPostProcessorClasses() {
|
||||
return beanFactoryPostProcessorClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent application context.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
if (applicationContext == null) {
|
||||
return;
|
||||
}
|
||||
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
|
||||
parent = (ConfigurableApplicationContext) applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ApplicationContext} from the provided path.
|
||||
*
|
||||
* @see ApplicationContextFactory#createApplicationContext()
|
||||
*/
|
||||
public ConfigurableApplicationContext createApplicationContext() {
|
||||
|
||||
if (resource == null) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
return new ResourceXmlApplicationContext(parent);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private final class ResourceXmlApplicationContext extends AbstractXmlApplicationContext {
|
||||
|
||||
private final DefaultListableBeanFactory parentBeanFactory;
|
||||
|
||||
/**
|
||||
* @param parent
|
||||
*/
|
||||
public ResourceXmlApplicationContext(ConfigurableApplicationContext parent) {
|
||||
super(parent);
|
||||
setId(generateId(resource));
|
||||
if (parent != null) {
|
||||
Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory,
|
||||
"The parent application context must have a bean factory of type DefaultListableBeanFactory");
|
||||
parentBeanFactory = (DefaultListableBeanFactory) parent.getBeanFactory();
|
||||
refreshBeanFactory();
|
||||
prepareContext(parent, this);
|
||||
}
|
||||
else {
|
||||
parentBeanFactory = null;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource
|
||||
* @return an identifier for the context
|
||||
*/
|
||||
private String generateId(Resource resource) {
|
||||
try {
|
||||
return resource.getURI().toString();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return resource.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
|
||||
super.customizeBeanFactory(beanFactory);
|
||||
if (parentBeanFactory != null) {
|
||||
ClassPathXmlApplicationContextFactory.this.prepareBeanFactory(parentBeanFactory, beanFactory);
|
||||
for (Class<? extends BeanFactoryPostProcessor> cls : beanFactoryPostProcessorClasses) {
|
||||
for (String name : parent.getBeanNamesForType(cls)) {
|
||||
beanFactory.registerSingleton(name, ((BeanFactoryPostProcessor) parent.getBean(name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resource[] getConfigResources() {
|
||||
return new Resource[] { resource };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceXmlApplicationContext:" + getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point for special subclasses that want to do more complex
|
||||
* things with the context prior to refresh. The default implementation
|
||||
* does nothing.
|
||||
*
|
||||
* @param parent the parent for the new application context
|
||||
* @param context the new application context before it is refreshed, but
|
||||
* after bean factory is initialized
|
||||
*
|
||||
* @see ClassPathXmlApplicationContextFactory#setBeanFactoryPostProcessorClasses(Class[])
|
||||
*/
|
||||
protected void prepareContext(ConfigurableApplicationContext parent, ConfigurableApplicationContext context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point for special subclasses that want to do more complex
|
||||
* things with the bean factory prior to refresh. The default implementation
|
||||
* copies all configuration from the parent according to the
|
||||
* {@link #setCopyConfiguration(boolean) flag} set.
|
||||
*
|
||||
* @param parent the parent bean factory for the new context (will never be
|
||||
* null)
|
||||
* @param beanFactory the new bean factory before bean definitions are
|
||||
* loaded
|
||||
*
|
||||
* @see ClassPathXmlApplicationContextFactory#setCopyConfiguration(boolean)
|
||||
* @see DefaultListableBeanFactory#copyConfigurationFrom(ConfigurableBeanFactory)
|
||||
*/
|
||||
protected void prepareBeanFactory(DefaultListableBeanFactory parent, DefaultListableBeanFactory beanFactory) {
|
||||
if (copyConfiguration && parent != null) {
|
||||
beanFactory.copyConfigurationFrom(parent);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BeanPostProcessor> beanPostProcessors = beanFactory.getBeanPostProcessors();
|
||||
for (BeanPostProcessor beanPostProcessor : new ArrayList<BeanPostProcessor>(beanPostProcessors)) {
|
||||
for (Class<?> cls : beanPostProcessorExcludeClasses) {
|
||||
if (cls.isAssignableFrom(beanPostProcessor.getClass())) {
|
||||
logger.debug("Removing bean post processor: " + beanPostProcessor + " of type " + cls);
|
||||
beanPostProcessors.remove(beanPostProcessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClassPathXmlApplicationContextFactory [resource=" + resource + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
return toString().equals(obj.toString());
|
||||
super(resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ClasspathXmlApplicationContextsFactoryBean implements FactoryBean,
|
||||
|
||||
/**
|
||||
* A set of resources to load using a
|
||||
* {@link ClassPathXmlApplicationContextFactory}. Each resource should be a
|
||||
* {@link GenericApplicationContextFactory}. Each resource should be a
|
||||
* Spring configuration file which is loaded into an application context
|
||||
* whose parent is the current context. In a configuration file the
|
||||
* resources can be given as a pattern (e.g.
|
||||
@@ -116,7 +116,7 @@ public class ClasspathXmlApplicationContextsFactoryBean implements FactoryBean,
|
||||
|
||||
List<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
|
||||
for (Resource resource : resources) {
|
||||
ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory();
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(resource);
|
||||
factory.setCopyConfiguration(copyConfiguration);
|
||||
if (beanFactoryPostProcessorClasses != null) {
|
||||
factory.setBeanFactoryPostProcessorClasses(beanFactoryPostProcessorClasses);
|
||||
@@ -124,7 +124,6 @@ public class ClasspathXmlApplicationContextsFactoryBean implements FactoryBean,
|
||||
if (beanPostProcessorExcludeClasses != null) {
|
||||
factory.setBeanPostProcessorExcludeClasses(beanPostProcessorExcludeClasses);
|
||||
}
|
||||
factory.setResource(resource);
|
||||
factory.setApplicationContext(applicationContext);
|
||||
applicationContextFactories.add(factory);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2006-2007 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.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextFactory} implementation that takes a parent context and a path to the context to create.
|
||||
* When createApplicationContext method is called, the child {@link ApplicationContext} will be returned. The child
|
||||
* context is not re-created every time it is requested, it is lazily initialized and cached. Clients should ensure that
|
||||
* it is closed when it is no longer needed.
|
||||
*
|
||||
*/
|
||||
public class GenericApplicationContextFactory extends AbstractApplicationContextFactory {
|
||||
|
||||
/**
|
||||
* Create an application context factory for the resource specified. The resource can be an actual {@link Resource},
|
||||
* in which case it will be interpreted as an XML file, or it can be a @Configuration class, or a package name.
|
||||
*
|
||||
* @param resource a resource (XML configuration file, @Configuration class or java package to scan)
|
||||
*/
|
||||
public GenericApplicationContextFactory(Object resource) {
|
||||
super(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AbstractApplicationContextFactory#createApplicationContext(ConfigurableApplicationContext, Object)
|
||||
*/
|
||||
@Override
|
||||
protected ConfigurableApplicationContext createApplicationContext(ConfigurableApplicationContext parent,
|
||||
Object resource) {
|
||||
if (resource instanceof Resource) {
|
||||
return new ResourceXmlApplicationContext(parent, (Resource) resource);
|
||||
}
|
||||
if (resource instanceof Class<?>) {
|
||||
return new ResourceAnnotationApplicationContext(parent, (Class<?>) resource);
|
||||
}
|
||||
if (resource instanceof String) {
|
||||
return new ResourceAnnotationApplicationContext(parent, (String) resource);
|
||||
}
|
||||
throw new IllegalArgumentException("No application context could be created for resource type: "
|
||||
+ resource.getClass());
|
||||
}
|
||||
|
||||
private abstract class ApplicationContextHelper {
|
||||
|
||||
private final DefaultListableBeanFactory parentBeanFactory;
|
||||
|
||||
private final ConfigurableApplicationContext parent;
|
||||
|
||||
public ApplicationContextHelper(ConfigurableApplicationContext parent, GenericApplicationContext context,
|
||||
Object config) {
|
||||
this.parent = parent;
|
||||
if (parent != null) {
|
||||
Assert.isTrue(parent.getBeanFactory() instanceof DefaultListableBeanFactory,
|
||||
"The parent application context must have a bean factory of type DefaultListableBeanFactory");
|
||||
parentBeanFactory = (DefaultListableBeanFactory) parent.getBeanFactory();
|
||||
}
|
||||
else {
|
||||
parentBeanFactory = null;
|
||||
}
|
||||
context.setParent(parent);
|
||||
context.setId(generateId(config));
|
||||
loadConfiguration(config);
|
||||
prepareContext(parent, context);
|
||||
}
|
||||
|
||||
protected abstract String generateId(Object config);
|
||||
|
||||
protected abstract void loadConfiguration(Object config);
|
||||
|
||||
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
if (parentBeanFactory != null) {
|
||||
GenericApplicationContextFactory.this.prepareBeanFactory(parentBeanFactory, beanFactory);
|
||||
for (Class<? extends BeanFactoryPostProcessor> cls : getBeanFactoryPostProcessorClasses()) {
|
||||
for (String name : parent.getBeanNamesForType(cls)) {
|
||||
beanFactory.registerSingleton(name, ((BeanFactoryPostProcessor) parent.getBean(name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class ResourceXmlApplicationContext extends GenericXmlApplicationContext {
|
||||
|
||||
private final ApplicationContextHelper helper;
|
||||
|
||||
/**
|
||||
* @param parent
|
||||
*/
|
||||
public ResourceXmlApplicationContext(ConfigurableApplicationContext parent, Resource resource) {
|
||||
helper = new ApplicationContextHelper(parent, this, resource) {
|
||||
@Override
|
||||
protected String generateId(Object config) {
|
||||
Resource resource = (Resource) config;
|
||||
try {
|
||||
return resource.getURI().toString();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return resource.toString();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void loadConfiguration(Object config) {
|
||||
Resource resource = (Resource) config;
|
||||
load(resource);
|
||||
}
|
||||
};
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
super.prepareBeanFactory(beanFactory);
|
||||
helper.prepareBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceXmlApplicationContext:" + getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class ResourceAnnotationApplicationContext extends AnnotationConfigApplicationContext {
|
||||
|
||||
private final ApplicationContextHelper helper;
|
||||
|
||||
public ResourceAnnotationApplicationContext(ConfigurableApplicationContext parent, Object resource) {
|
||||
helper = new ApplicationContextHelper(parent, this, resource) {
|
||||
@Override
|
||||
protected String generateId(Object config) {
|
||||
if (config instanceof Class) {
|
||||
Class<?> type = (Class<?>) config;
|
||||
return type.getName();
|
||||
}
|
||||
else {
|
||||
return config.toString();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void loadConfiguration(Object config) {
|
||||
if (config instanceof Class) {
|
||||
Class<?> type = (Class<?>) config;
|
||||
register(type);
|
||||
}
|
||||
else {
|
||||
String pkg = (String) config;
|
||||
scan(pkg);
|
||||
}
|
||||
}
|
||||
};
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
super.prepareBeanFactory(beanFactory);
|
||||
helper.prepareBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceAnnotationApplicationContext:" + getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class SimpleJobBuilder extends JobBuilderHelper<SimpleJobBuilder> {
|
||||
if (builder != null) {
|
||||
return builder.end().build();
|
||||
}
|
||||
SimpleJob job = new SimpleJob();
|
||||
SimpleJob job = new SimpleJob(getName());
|
||||
super.enhance(job);
|
||||
job.setSteps(steps);
|
||||
try {
|
||||
|
||||
@@ -27,8 +27,8 @@ import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.configuration.DuplicateJobException;
|
||||
import org.springframework.batch.core.configuration.JobFactory;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactory;
|
||||
import org.springframework.batch.core.configuration.support.DefaultJobLoader;
|
||||
import org.springframework.batch.core.configuration.support.GenericApplicationContextFactory;
|
||||
import org.springframework.batch.core.configuration.support.JobLoader;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
@@ -133,9 +133,8 @@ public class JobRegistryBackgroundJobRunner {
|
||||
Resource path = resources[j];
|
||||
logger.info("Registering Job definitions from " + Arrays.toString(resources));
|
||||
|
||||
ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory();
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(path);
|
||||
factory.setApplicationContext(parentContext);
|
||||
factory.setResource(path);
|
||||
jobLoader.load(factory);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@Configuration
|
||||
public class DataSourceConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@PostConstruct
|
||||
protected void initialize() {
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
populator.addScript(resourceLoader.getResource(ClassUtils.addResourcePathToPackagePath(Step.class, "schema-hsqldb.sql")));
|
||||
populator.setContinueOnError(true);
|
||||
DatabasePopulatorUtils.execute(populator, dataSource());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
return new EmbeddedDatabaseFactory().getDatabase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2006-2011 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 static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
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.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JobBuilderConfigurationTests {
|
||||
|
||||
public static boolean fail = false;
|
||||
|
||||
private JobExecution execution;
|
||||
|
||||
@Test
|
||||
public void testVanillaBatchConfiguration() throws Exception {
|
||||
testJob(BatchStatus.COMPLETED, 2, TestConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurerAsConfiguration() throws Exception {
|
||||
testJob(BatchStatus.COMPLETED, 1, TestConfigurer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurerAsBean() throws Exception {
|
||||
testJob(BatchStatus.COMPLETED, 1, BeansConfigurer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoConfigurations() throws Exception {
|
||||
testJob("testJob", BatchStatus.COMPLETED, 2, TestConfiguration.class, AnotherConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoConfigurationsAndConfigurer() throws Exception {
|
||||
testJob("testJob", BatchStatus.COMPLETED, 2, TestConfiguration.class, TestConfigurer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoConfigurationsAndBeansConfigurer() throws Exception {
|
||||
testJob("testJob", BatchStatus.COMPLETED, 2, TestConfiguration.class, BeansConfigurer.class);
|
||||
}
|
||||
|
||||
private void testJob(BatchStatus status, int stepExecutionCount, Class<?> config) throws Exception {
|
||||
testJob(null, status, stepExecutionCount, config);
|
||||
}
|
||||
|
||||
private void testJob(String jobName, BatchStatus status, int stepExecutionCount, Class<?>... config)
|
||||
throws Exception {
|
||||
|
||||
Class<?>[] configs = new Class<?>[config.length + 1];
|
||||
System.arraycopy(config, 0, configs, 1, config.length);
|
||||
configs[0] = DataSourceConfiguration.class;
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configs);
|
||||
Job job = jobName == null ? context.getBean(Job.class) : context.getBean(jobName, Job.class);
|
||||
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
|
||||
execution = jobLauncher
|
||||
.run(job, new JobParametersBuilder().addLong("run.id", (long) (Math.random() * Long.MAX_VALUE))
|
||||
.toJobParameters());
|
||||
assertEquals(status, execution.getStatus());
|
||||
assertEquals(stepExecutionCount, execution.getStepExecutions().size());
|
||||
context.close();
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobs;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
@Bean
|
||||
public Job testJob() throws Exception {
|
||||
SimpleJobBuilder builder = jobs.get("test").start(step1()).next(step2());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step1() throws Exception {
|
||||
return steps.get("step1").tasklet(tasklet()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step2() throws Exception {
|
||||
return steps.get("step2").tasklet(tasklet()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Tasklet tasklet() {
|
||||
return new Tasklet() {
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception {
|
||||
if (fail) {
|
||||
throw new RuntimeException("Planned!");
|
||||
}
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class AnotherConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobs;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
@Autowired
|
||||
private Tasklet tasklet;
|
||||
|
||||
@Bean
|
||||
public Job anotherJob() throws Exception {
|
||||
SimpleJobBuilder builder = jobs.get("another").start(step3());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step3() throws Exception {
|
||||
return steps.get("step3").tasklet(tasklet).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class TestConfigurer extends DefaultBatchConfigurer {
|
||||
|
||||
@Autowired
|
||||
private SimpleBatchConfiguration jobs;
|
||||
|
||||
@Bean
|
||||
public Job testConfigererJob() throws Exception {
|
||||
SimpleJobBuilder builder = jobs.jobBuilders().get("configurer").start(step1());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step1() throws Exception {
|
||||
AbstractStep step = new AbstractStep("step1") {
|
||||
@Override
|
||||
protected void doExecute(StepExecution stepExecution) throws Exception {
|
||||
stepExecution.setExitStatus(ExitStatus.COMPLETED);
|
||||
stepExecution.setStatus(BatchStatus.COMPLETED);
|
||||
}
|
||||
};
|
||||
step.setJobRepository(getJobRepository());
|
||||
return step;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public static class BeansConfigurer {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobs;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
@Bean
|
||||
public Job beansConfigurerJob() throws Exception {
|
||||
SimpleJobBuilder builder = jobs.get("beans").start(step1());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step1() throws Exception {
|
||||
return steps.get("step1").tasklet(new Tasklet() {
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Autowired
|
||||
protected BatchConfigurer configurer(DataSource dataSource) {
|
||||
return new DefaultBatchConfigurer(dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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 static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
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.JobParametersBuilder;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.configuration.JobLocator;
|
||||
import org.springframework.batch.core.configuration.support.ApplicationContextFactory;
|
||||
import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar;
|
||||
import org.springframework.batch.core.configuration.support.GenericApplicationContextFactory;
|
||||
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
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.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JobLoaderConfigurationTests {
|
||||
|
||||
private JobExecution execution;
|
||||
|
||||
@Test
|
||||
public void testJobLoader() throws Exception {
|
||||
testJob("test", BatchStatus.COMPLETED, 2, LoaderFactoryConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobLoaderWithArray() throws Exception {
|
||||
testJob("test", BatchStatus.COMPLETED, 2, LoaderRegistrarConfiguration.class);
|
||||
}
|
||||
|
||||
private void testJob(String jobName, BatchStatus status, int stepExecutionCount, Class<?>... config)
|
||||
throws Exception {
|
||||
|
||||
Class<?>[] configs = new Class<?>[config.length + 1];
|
||||
System.arraycopy(config, 0, configs, 1, config.length);
|
||||
configs[0] = DataSourceConfiguration.class;
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configs);
|
||||
Job job = jobName == null ? context.getBean(Job.class) : context.getBean(JobLocator.class).getJob(jobName);
|
||||
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
|
||||
execution = jobLauncher
|
||||
.run(job, new JobParametersBuilder().addLong("run.id", (long) (Math.random() * Long.MAX_VALUE))
|
||||
.toJobParameters());
|
||||
assertEquals(status, execution.getStatus());
|
||||
assertEquals(stepExecutionCount, execution.getStepExecutions().size());
|
||||
context.close();
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing(modular=true)
|
||||
public static class LoaderFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApplicationContextFactory jobContextFactory() {
|
||||
return new GenericApplicationContextFactory(TestConfiguration.class);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationContextFactory vanillaContextFactory() {
|
||||
return new GenericApplicationContextFactory(VanillaConfiguration.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing(modular=true)
|
||||
public static class LoaderRegistrarConfiguration {
|
||||
|
||||
@Autowired
|
||||
private AutomaticJobRegistrar registrar;
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
registrar.addApplicationContextFactory(new GenericApplicationContextFactory(TestConfiguration.class));
|
||||
registrar.addApplicationContextFactory(new GenericApplicationContextFactory(VanillaConfiguration.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobs;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
@Bean
|
||||
public Job testJob() throws Exception {
|
||||
SimpleJobBuilder builder = jobs.get("test").start(step1()).next(step2());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step1() throws Exception {
|
||||
return steps.get("step1").tasklet(tasklet()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step2() throws Exception {
|
||||
return steps.get("step2").tasklet(tasklet()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Tasklet tasklet() {
|
||||
return new Tasklet() {
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception {
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class VanillaConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobs;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
@Bean
|
||||
public Job vanillaJob() throws Exception {
|
||||
SimpleJobBuilder builder = jobs.get("vanilla").start(step3());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step3() throws Exception {
|
||||
return steps.get("step3").tasklet(new Tasklet() {
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception {
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
}).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -190,8 +190,7 @@ public class AutomaticJobRegistrarTests {
|
||||
private void setUpApplicationContextFactories(Resource[] jobPaths, ApplicationContext parent) {
|
||||
Collection<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
|
||||
for (Resource resource : jobPaths) {
|
||||
ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory();
|
||||
factory.setResource(resource);
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(resource);
|
||||
factory.setApplicationContext(parent);
|
||||
applicationContextFactories.add(factory);
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ public class DefaultJobLoaderTests {
|
||||
|
||||
@Test
|
||||
public void testLoadWithExplicitName() throws Exception {
|
||||
ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(
|
||||
new ByteArrayResource(JOB_XML.getBytes()));
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ByteArrayResource(
|
||||
JOB_XML.getBytes()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
jobLoader.reload(factory);
|
||||
@@ -48,8 +48,8 @@ public class DefaultJobLoaderTests {
|
||||
|
||||
@Test
|
||||
public void testReload() throws Exception {
|
||||
ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(
|
||||
new ClassPathResource("trivial-context.xml", getClass()));
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(
|
||||
"trivial-context.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
jobLoader.reload(factory);
|
||||
@@ -58,8 +58,8 @@ public class DefaultJobLoaderTests {
|
||||
|
||||
@Test
|
||||
public void testReloadWithAutoRegister() throws Exception {
|
||||
ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory(
|
||||
new ClassPathResource("trivial-context-autoregister.xml", getClass()));
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(
|
||||
"trivial-context-autoregister.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
jobLoader.reload(factory);
|
||||
@@ -67,9 +67,8 @@ public class DefaultJobLoaderTests {
|
||||
}
|
||||
|
||||
private static final String JOB_XML = String
|
||||
.format(
|
||||
"<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
|
||||
+ "xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd'><bean class='%s$StubJob'/></beans>",
|
||||
.format("<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
|
||||
+ "xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd'><bean class='%s$StubJob'/></beans>",
|
||||
DefaultJobLoaderTests.class.getName());
|
||||
|
||||
public static class StubJob implements Job {
|
||||
|
||||
@@ -32,32 +32,30 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ClassPathXmlApplicationContextFactoryTests {
|
||||
|
||||
private ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory();
|
||||
public class GenericApplicationContextFactoryTests {
|
||||
|
||||
@Test
|
||||
public void testCreateJob() {
|
||||
factory.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"trivial-context.xml")));
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "trivial-context.xml")));
|
||||
ConfigurableApplicationContext context = factory.createApplicationContext();
|
||||
assertNotNull(context);
|
||||
assertTrue("Wrong id: "+context, context.getId().contains("trivial-context.xml"));
|
||||
assertTrue("Wrong id: " + context, context.getId().contains("trivial-context.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetJobName() {
|
||||
factory.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"trivial-context.xml")));
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "trivial-context.xml")));
|
||||
assertEquals("test-job", factory.createApplicationContext().getBeanNamesForType(Job.class)[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentConfigurationInherited() {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml")));
|
||||
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(
|
||||
getClass(), "parent-context.xml")));
|
||||
factory.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"child-context.xml")));
|
||||
ConfigurableApplicationContext context = factory.createApplicationContext();
|
||||
assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]);
|
||||
assertEquals("bar", ((Job) context.getBean("test-job", Job.class)).getName());
|
||||
@@ -66,10 +64,10 @@ public class ClassPathXmlApplicationContextFactoryTests {
|
||||
|
||||
@Test
|
||||
public void testBeanFactoryPostProcessorOrderRespected() {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "placeholder-context.xml")));
|
||||
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(
|
||||
getClass(), "parent-context.xml")));
|
||||
factory.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"placeholder-context.xml")));
|
||||
ConfigurableApplicationContext context = factory.createApplicationContext();
|
||||
assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]);
|
||||
assertEquals("spam", ((Job) context.getBean("test-job", Job.class)).getName());
|
||||
@@ -77,10 +75,10 @@ public class ClassPathXmlApplicationContextFactoryTests {
|
||||
|
||||
@Test
|
||||
public void testBeanFactoryPostProcessorsNotCopied() {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(), "child-context.xml")));
|
||||
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(
|
||||
getClass(), "parent-context.xml")));
|
||||
factory.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"child-context.xml")));
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends BeanFactoryPostProcessor>[] classes = (Class<? extends BeanFactoryPostProcessor>[]) new Class<?>[0];
|
||||
factory.setBeanFactoryPostProcessorClasses(classes);
|
||||
@@ -92,10 +90,10 @@ public class ClassPathXmlApplicationContextFactoryTests {
|
||||
|
||||
@Test
|
||||
public void testBeanFactoryConfigurationNotCopied() {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"child-context.xml")));
|
||||
factory.setApplicationContext(new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(
|
||||
getClass(), "parent-context.xml")));
|
||||
factory.setResource(new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"child-context.xml")));
|
||||
factory.setCopyConfiguration(false);
|
||||
ConfigurableApplicationContext context = factory.createApplicationContext();
|
||||
assertEquals("test-job", context.getBeanNamesForType(Job.class)[0]);
|
||||
@@ -109,9 +107,8 @@ public class ClassPathXmlApplicationContextFactoryTests {
|
||||
public void testEquals() throws Exception {
|
||||
Resource resource = new ClassPathResource(ClassUtils.addResourcePathToPackagePath(getClass(),
|
||||
"child-context.xml"));
|
||||
factory.setResource(resource);
|
||||
ClassPathXmlApplicationContextFactory other = new ClassPathXmlApplicationContextFactory();
|
||||
other.setResource(resource);
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(resource);
|
||||
GenericApplicationContextFactory other = new GenericApplicationContextFactory(resource);
|
||||
assertEquals(other, factory);
|
||||
assertEquals(other.hashCode(), factory.hashCode());
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<property name="name" value="${foo}" />
|
||||
</bean>
|
||||
|
||||
<bean id="foo" class="org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactoryTests$Foo">
|
||||
<bean id="foo" class="org.springframework.batch.core.configuration.support.GenericApplicationContextFactoryTests$Foo">
|
||||
<property name="values" value="1,4" />
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -260,8 +260,8 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.sample.config;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp.BasicDataSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@PropertySource("classpath:/batch-hsql.properties")
|
||||
public class DataSourceConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@PostConstruct
|
||||
protected void initialize() {
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
populator.addScript(resourceLoader.getResource(environment.getProperty("batch.schema.script")));
|
||||
populator.setContinueOnError(true);
|
||||
DatabasePopulatorUtils.execute(populator , dataSource());
|
||||
}
|
||||
|
||||
@Bean(destroyMethod="close")
|
||||
public DataSource dataSource() {
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
dataSource.setDriverClassName(environment.getProperty("batch.jdbc.driver"));
|
||||
dataSource.setUrl(environment.getProperty("batch.jdbc.url"));
|
||||
dataSource.setUsername(environment.getProperty("batch.jdbc.user"));
|
||||
dataSource.setPassword(environment.getProperty("batch.jdbc.password"));
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.sample.config;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.sample.domain.trade.Trade;
|
||||
import org.springframework.batch.sample.domain.trade.internal.GeneratingTradeItemReader;
|
||||
import org.springframework.batch.sample.support.RetrySampleItemWriter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class RetrySampleConfiguration {
|
||||
|
||||
@Autowired
|
||||
private JobBuilderFactory jobs;
|
||||
|
||||
@Autowired
|
||||
private StepBuilderFactory steps;
|
||||
|
||||
@Bean
|
||||
public Job retrySample() throws Exception {
|
||||
return jobs.get("retrySample").start(step()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected Step step() throws Exception {
|
||||
return steps.get("step").<Trade, Object> chunk(1).reader(reader()).writer(writer()).faultTolerant()
|
||||
.retry(Exception.class).retryLimit(3).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected ItemReader<Trade> reader() {
|
||||
GeneratingTradeItemReader reader = new GeneratingTradeItemReader();
|
||||
reader.setLimit(10);
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected ItemWriter<Object> writer() {
|
||||
return new RetrySampleItemWriter<Object>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<batch:job-repository />
|
||||
|
||||
<job id="tradeJob" xmlns="http://www.springframework.org/schema/batch">
|
||||
<step id="step1" next="step2">
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.batch.sample;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.sample.config.DataSourceConfiguration;
|
||||
import org.springframework.batch.sample.config.JobRunnerConfiguration;
|
||||
import org.springframework.batch.sample.config.RetrySampleConfiguration;
|
||||
import org.springframework.batch.sample.domain.trade.internal.GeneratingTradeItemReader;
|
||||
import org.springframework.batch.sample.support.RetrySampleItemWriter;
|
||||
import org.springframework.batch.test.JobLauncherTestUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Checks that expected number of items have been processed.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = { DataSourceConfiguration.class, RetrySampleConfiguration.class, JobRunnerConfiguration.class})
|
||||
public class RetrySampleConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private GeneratingTradeItemReader itemGenerator;
|
||||
|
||||
@Autowired
|
||||
private RetrySampleItemWriter<?> itemProcessor;
|
||||
|
||||
@Autowired
|
||||
private JobLauncherTestUtils jobLauncherTestUtils;
|
||||
|
||||
@Test
|
||||
public void testLaunchJob() throws Exception {
|
||||
jobLauncherTestUtils.launchJob();
|
||||
//items processed = items read + 2 exceptions
|
||||
assertEquals(itemGenerator.getLimit()+2, itemProcessor.getCounter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.sample.config;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.test.JobLauncherTestUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public class JobRunnerConfiguration {
|
||||
|
||||
@Bean
|
||||
public JobLauncherTestUtils utils() throws Exception {
|
||||
return new JobLauncherTestUtils();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user