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:
Dave Syer
2012-12-08 18:35:13 +00:00
committed by Michael Minella
parent 96de2dc4e9
commit 25cd6d6826
29 changed files with 1959 additions and 345 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 &#064;Configuration
* class, roughly equivalent to using the {@code <batch:*>} XML namespace.
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableBatchProcessing
* &#064;Import(DataSourceCnfiguration.class)
* public class AppConfig {
*
* &#064;Autowired
* private JobBuilderFactory jobs;
*
* &#064;Bean
* public Job job() {
* return jobs.get(&quot;myJob&quot;).start(step1()).next(step2()).build();
* }
*
* &#064;Bean
* protected Step step1() {
* ...
* }
*
* &#064;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">
* &#064;Configuration
* &#064;EnableBatchProcessing
* public class AppConfig extends DefaultBatchConfigurer {
*
* &#064;Bean
* public Job job() {
* ...
* }
*
* &#064;Override
* protected JobRepository createJobRepository() {
* ...
* }
*
* ...
*
* }
* </pre>
*
* Note that only one of your configuration classes needs to have the <code>&#064;EnableBatchProcessing</code>
* annotation. Once you have an <code>&#064;EnableBatchProcessing</code> class in your configuration you will have an
* instance of {@link StepScope} so your beans inside steps can have <code>&#064;Scope("step")</code>. You will also be
* able to <code>&#064;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">
* &#064;Configuration
* &#064;EnableBatchProcessing(modular=true)
* public class AppConfig {
*
* &#064;Bean
* public ApplicationContextFactory someJobs() {
* return new GenericApplicationContextFactory(SomeJobConfiguration.class);
* }
*
* &#064;Bean
* public ApplicationContextFactory moreJobs() {
* return new GenericApplicationContextFactory(MoreJobConfiguration.class);
* }
*
* ...
*
* }
* </pre>
*
* Note that a modular parent context in general should <em>not</em> itself contain &#64;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 &#64;Bean Job definitions in this context, but rather supply them in separate (child)
* contexts through an {@link ApplicationContextFactory}.
*/
boolean modular() default false;
}

View File

@@ -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 &#64;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;
}
}

View File

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

View File

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

View File

@@ -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 &#64;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;
}
}

View File

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

View File

@@ -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()
*/

View File

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

View File

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

View File

@@ -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 &#64;Configuration class, or a package name.
*
* @param resource a resource (XML configuration file, &#064;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();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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