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:
1@Configuration
2@EnableBatchProcessing
3@PropertySource("classpath:batch.properties")
4public class WebsphereInfrastructureConfiguration implements BatchConfigurer, InfrastructureConfiguration {
5
6 @Autowired
7 private Environment env;
8
9 @Bean
10 public DataSource dataSource(){
11 try {
12 InitialContext initialContext = new InitialContext();
13 return (DataSource) initialContext.lookup(env.getProperty("datasource.jndi"));
14 } catch (NamingException e) {
15 throw new RuntimeException("JNDI lookup failed.",e);
16 }
17 }
18
19 public JobRepository getJobRepository() throws Exception {
20 JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
21 factory.setDataSource(dataSource());
22 factory.setTransactionManager(getTransactionManager());
23 factory.afterPropertiesSet();
24 return (JobRepository) factory.getObject();
25 }
26
27 public PlatformTransactionManager getTransactionManager() throws Exception {
28 return new WebSphereUowTransactionManager();
29 }
30
31 public JobLauncher getJobLauncher() throws Exception {
32 SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
33 jobLauncher.setJobRepository(getJobRepository());
34 jobLauncher.afterPropertiesSet();
35 return jobLauncher;
36 }
37
38}
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 PropertySource
s, for example there are automatically PropertySource
s 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.
More articles
fromTobias Flohre
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Tobias Flohre
Senior Software Developer
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.