Overview

Spring Batch 2.2 – JavaConfig Part 3: Profiles and environments

4 Comments

This is the third post about the new Java based configuration features in Spring Batch 2.2. In the first post I compared the two configuration styles on a non-trivial batch job reading from a file and writing to a database. I used a very simple infrastructure setup with an In-Memory-Database and a DataSourceTransactionManager, and I didn’t have any property files to read configurable data from. This post is about adding another infrastructure configuration for a production environment.
In future posts I will talk about job inheritance, modular configurations and partitioning and multi-threaded step, everything regarding Java based configuration, of course. You can find the JavaConfig code examples on Github.

In production our batch jobs run on an application server, in this example the Websphere Application Server. The DataSource is configured in the application server and can be accessed via JNDI. We want to use the application server’s transaction features to be able to have XA transactions, so a DataSourceTransactionManager won’t be sufficient this time. The JNDI name of the DataSource shall be read from a properties file to make it configurable (you may argue that this doesn’t make too much sense, and I agree, but I wanna show how reading properties works).
Here’s the configuration:

@Configuration
@EnableBatchProcessing
@PropertySource("classpath:batch.properties")
public class WebsphereInfrastructureConfiguration implements BatchConfigurer, InfrastructureConfiguration {
 
	@Autowired
	private Environment env;
 
	@Bean
	public DataSource dataSource(){
		try {
			InitialContext initialContext = new InitialContext();
			return (DataSource) initialContext.lookup(env.getProperty("datasource.jndi"));
		} catch (NamingException e) {
			throw new RuntimeException("JNDI lookup failed.",e);
		}
	}
 
	public JobRepository getJobRepository() throws Exception {
		JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
		factory.setDataSource(dataSource());
		factory.setTransactionManager(getTransactionManager());
		factory.afterPropertiesSet();
		return  (JobRepository) factory.getObject();
	}
 
	public PlatformTransactionManager getTransactionManager() throws Exception {
		return new WebSphereUowTransactionManager();
	}
 
	public JobLauncher getJobLauncher() throws Exception {
		SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
		jobLauncher.setJobRepository(getJobRepository());
		jobLauncher.afterPropertiesSet();
		return jobLauncher;
	}
 
}

I’ll walk through the details now.

Implementing BatchConfigurer

The BatchConfigurer interface allows for creating our own JobRepository, JobLauncher and PlatformTransactionManager. If you’re not adding an implementation of the interface to your ApplicationContext (and you do that by implementing it in the configuration class since the configuration itself is a Spring bean), the annotation @EnableBatchProcessing creates a DefaultBatchConfigurer component. This component expects exactly one DataSource in the ApplicationContext and creates a DataSourceTransactionManager. If you’re running your jobs on an application server, the DefaultBatchConfigurer won’t be sufficient, because you need to use the application server’s transaction manager. That’s done by using the WebSphereUowTransactionManager in the example above. It’s also very common to have more than one DataSource in an application server environment, which would be another problem with the default behaviour.
The DataSource is looked up via JNDI this time instead of creating it directly.

Environment and @PropertySource

With the annotation @PropertySource you may specify files as sources for properties, in this case we look for a file named batch.properties on the classpath and add all properties in it to Spring’s Environment. The Environment can be injected into configuration classes to use those properties. In the example above we take the property datasource.jndi and use it as the DataSource‘s JNDI name. Properties may come from many different kinds of PropertySources, for example there are automatically PropertySources for environment variables and JVM properties registered when you start an ApplicationContext.

Implementing InfrastructureConfiguration

We have the interface InfrastructureConfiguration, and of course we’re gonna implement it this time as well. As you might remember we need an InfrastructureConfiguration in our job configuration, but the job configuration doesn’t care about the implementation – perfect exchangeability! We can use the job configuration with all kinds of infrastructure configurations as long as the contract InfrastructureConfiguration is met.

Conclusion

We saw what to do if the default behaviour of the annotation @EnableBatchProcessing is not enough: add an implementation of BatchConfigurer to your ApplicationContext. We saw how to use properties in Java based configuration.
And again, we saw one advantage of Java based configuration: the possibility to define a contract for a configuration with an interface, in this case the InfrastructureConfiguration. Configuration code that uses that configuration doesn’t have to care about the implementation, and we may add new implementations like the one in this post without affecting the other configuration classes.

Kommentare

  • Michel Jung

    18. December 2013 von Michel Jung

    Thanks for the summary. However, it looks pretty dangerous to me;

    In DefaultBatchConfigurer, the launcher, repository and transaction manager are explicitly created in @PostConstruct to ensure only a single instance exists. In your configuration, each time the getters are called, a new instance is created.

    Wouldn’t it be safer to do the same one-time initialization as DefaultBatchConfigurer does?

    • Tobias Flohre

      18. December 2013 von Tobias Flohre

      Hi Michel,

      first thing: I wouldn’t call it dangerous, because all of those components are stateless, so it doesn’t really matter how many instances exist.
      But, second thing: if you don’t call those methods, there won’t be more than one instance each. The Simple-/ModularBatchConfiguration uses the BatchConfigurer to create the beans, and they call those getters exactly once (you probably know that @Bean methods are only executed once, even if they are called more often).
      If you need a JobRepository, TransactionManager or JobLauncher in one of your components, it would be the most accurate thing to autowire the AbstractBatchConfiguration into your configuration class and call batchConfig.jobRepository() to get the JobRepository if you want to set it in your component.

  • Imen

    Thank you for this detailed example, I’ll be so grateful if you give me any suggestions about the way to transform this xml config to javaconfig :


    I was able to use your idea to create a job having one step leading to another step on success and to a different step on failure:

    SimpleJobBuilder builder = new JobBuilder("job").repository(jobRepository)
    .start(step1()).next(step2())
    .on("FAILED").to(step3()).build();

Comment

Your email address will not be published. Required fields are marked *