Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revisit the configuration of infrastructure beans with @EnableBatchProcessing #3942

Closed
fmbenhassine opened this issue Jun 16, 2021 · 1 comment

Comments

@fmbenhassine
Copy link
Contributor

fmbenhassine commented Jun 16, 2021

Compared to the XML configuration style where infrastructure beans (JobRepository, JobLauncher, etc) should be defined manually, @EnableBatchProcessing does a good job in configuring those beans automatically and making them available for autowiring in users configuration classes. However, several issues have been reported regarding the default behaviour of this annotation as well as the customization of its behaviour. Here is a non exhaustive list:

1. Customization of infrastructure beans is not straightforward

For example, as reported in #3765, in order to create a custom serializer, one needs to provide a custom JobRepository. Now in order to provide a custom JobRepository, one needs to provide a custom BatchConfigurer (either by implementing the interface or by extending the default one and override a method), something like:

@Configuration
@EnableBatchProcessing
public class MyJobConfigWithCustomSerializer {

    @Bean
    public BatchConfigurer batchConfigurer() {
        return new DefaultBatchConfigurer() {
            @Override
            public JobRepository getJobRepository() {
                ExecutionContextSerializer serializer = new Jackson2ExecutionContextStringSerializer();
                // customize serializer
                JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
                factory.setSerializer(serializer);
                // set other properties on the factory bean
                try {
                    factory.afterPropertiesSet();
                    return factory.getObject();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

}

Moreover, in this case, the custom serializer should also be set on the JobExplorer in order to correctly deserialize the execution context while exploring meta-data that was persisted with the JobRepository. So one needs to do the following as well:

@Configuration
@EnableBatchProcessing
public class MyJobConfigWithCustomSerializer {

    @Bean
    public BatchConfigurer batchConfigurer() {
        return new DefaultBatchConfigurer() {
            @Override
            public JobRepository getJobRepository() {
                JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
                factory.setSerializer(createCustomSerializer());
                // set other properties on the factory bean
                try {
                    factory.afterPropertiesSet();
                    return factory.getObject();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public JobExplorer getJobExplorer() {
                JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
                factoryBean.setSerializer(createCustomSerializer());
                // set other properties on the factory bean
                try {
                    factoryBean.afterPropertiesSet();
                    return factoryBean.getObject();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            private ExecutionContextSerializer createCustomSerializer() {
                Jackson2ExecutionContextStringSerializer serializer = new Jackson2ExecutionContextStringSerializer();
                // customize serializer
                return serializer;
            }
        };
    }
    
}

The process is the same for other properties of the job repository/explorer like the tables prefix, LobHandler, etc.

2. Unconditional exposure of some beans

While batch specific beans like JobRepository, JobLauncher, etc could be exposed in the application context "safely", some beans like the transaction manager could be used in other parts of the application and exposing it unconditionally could be problematic (see #816). This is especially true when using Spring Boot, and this requires bean overriding which is not always wanted by users.

3. Extending infrastructure beans is not straightforward / possible

Since infrastructure beans are systematically defined and exposed by @EnableBatchProcessing and not looked up from the application context first, it is not easy/possible to extend those beans to add custom behaviour (like adding tracing to the JobRepository for instance, as reported in #3899) and use the extensions in place of default beans.

4. BatchConfigurer is eventually an unnecessary level of indirection

Most people tend to declare infrastructure beans in the application context and expect them to be picked up by Spring Batch (this is not a wrong expectation). Here are some examples:

If @EnableBatchProcessing is changed to look for beans in the application context first, requiring users to provide a custom BatchConfigurer would not become mandatory anymore. For example, the following way of configuring a custom JobRepository:

@Configuration
@EnableBatchProcessing
public class MyJobConfigWithCustomJobRepository {

    @Bean
    public BatchConfigurer batchConfigurer() {
        return new DefaultBatchConfigurer() {
            @Override
            public JobRepository getJobRepository() {
                JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
                // set properties on the factory bean
                try {
                    factory.afterPropertiesSet();
                    return factory.getObject();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

   // job bean definition
}

could become:

@Configuration
@EnableBatchProcessing
public class MyJobConfigWithCustomJobRepository {

    @Bean
    public JobRepository jobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        // set properties on the factory bean
        factory.afterPropertiesSet();
        return factory.getObject();
    }

   // job bean definition
   
}

5. Confusing configuration when batch meta-data is not required

The configuration of the JobRepository/JobExplorer in @EnableBatchProcessing is based on the presence of a DataSource bean (if no data source is provided, a Map-based job repository/explorer is configured, which was deprecated anyway #3780). If the application context contains one or more datasource that should not be used by Spring Batch for its meta-data, things seem to become complicated and confusing to many people. Here are some examples:

It is concerning that people end up with an empty setter for the datasource:

The data source is actually an implementation detail of a particular JobRepository implementation, which is the JDBC based JobRepository. Other implementations of JobRepository might not need a data source at all (like a MongoDB based job repository for example). The point here is that the data source should not be a first order concern in terms of configuration, but rather a second order concern. In other words, @EnableBatchProcessing should first make sure the user wants to use the JDBC based job repository, and if so, only then check for a data source bean in the application context.


Possible solutions

I see a couple of options here, but I'm open for other suggestions as well.

1. Use annotation attributes to customize infrastructure beans

The idea here is to make @EnableBatchProcessing first look for infrastructure beans in the application context (this is similar and consistent with the way other projects from the portfolio configure apps, like Spring Security for instance). If those beans are not defined, then create them and register them in the application context. The same naming conventions used with XML configuration style should be used for consistency. For example:

@Configuration
@EnableBatchProcessing(dataSource = "myDataSource",  // could be omitted if named "dataSource"
                       transactionManager = "myTransactionManager", // could be omitted if named "transactionManager"
                       serializer = "mySerializer")
public class MyJobConfiguration {

    @Bean
    public Job job(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory.get("myJob")
                // define job flow
                .build();
    }

    @Bean // could be the one auto-configured by Spring Boot
    public ExecutionContextSerializer mySerializer() {
        ExecutionContextSerializer serializer = new Jackson2ExecutionContextStringSerializer();
        // customize serializer
        return serializer;
    }

}

In this example, @EnableBatchProcessing would first look for a JobRepository bean named jobRepository (same naming convention as XML) in the application context. If no such bean is defined, then it should create one by setting collaborators as defined in the annotation attributes.

2. Provide a base configuration class with infrastructure beans

Similar to the base XML application context that defines infrastructure beans with XML configuration, the idea is to provide a similar mechanism but for Java configuration. This means providing a base configuration class (which could be something like the current AbstractBatchConfiguration but with ready-to-use bean definitions in it) that users can extend to define their batch jobs:

@Configuration
public class MyBatchApplication extends BatchConfiguration {

    @Bean
    public Job job(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory.get("job")
                // define job
                .build();
    }

}

Any bean that needs customization can be declared in the user's class.

@fmbenhassine fmbenhassine added this to the 5.0.0 milestone Jun 16, 2021
@fmbenhassine fmbenhassine self-assigned this Jun 16, 2021
fmbenhassine added a commit that referenced this issue Aug 17, 2021
This commit removes the deprecated Map-based job repository
and job explorer implementations with their respective DAOs.
Using the `EnableBatchProcessing` annotation now requires a
datasource bean to be defined in the application context.
This will be reviewed as part of #3942.

This commit is a first pass that updates related tests to use
the JDBC-based job repository/explorer with an embedded database.
A second pass should be done to improve tests by caching/reusing
embedded databases if possible.

Issue #3836
@fmbenhassine fmbenhassine modified the milestones: 5.0.0, 5.0.0-M6 Aug 31, 2022
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Sep 16, 2022
Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context

Issue spring-projects#3942
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Sep 16, 2022
Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context

Issue spring-projects#3942
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Sep 19, 2022
Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context
* AbstractBatchConfiguration is now intended to be extended
by users code to get/customize basic infrastructure beans

Issue spring-projects#3942
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Sep 20, 2022
Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context
* AbstractBatchConfiguration is now intended to be extended
by users code to get/customize basic infrastructure beans

Issue spring-projects#3942
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Sep 20, 2022
Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context
* AbstractBatchConfiguration is now intended to be extended
by users code to get/customize basic infrastructure beans

Issue spring-projects#3942
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Sep 20, 2022
Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context
* AbstractBatchConfiguration is now intended to be extended
by users code to get/customize basic infrastructure beans

Issue spring-projects#3942

Revisit the configuration code of EnableBatchProcessing

Before this commit, the configuration of infrastructure beans
was confusing and not straightforward to customize. This commit
changes the way Batch infrastructure beans are configured.
The most important changes are:

* EnableBatchProcessing now provides new attributes to
configure properties of infrastructure beans
* Bean registration is now done programmatically with a
BeanDefinitionRegistrar instead of importing a class with
statically annotated bean definition methods
* Bean are now resolved from the application context directly
instead of being resolved from a BatchConfigurer
* Both a data source and a transaction manager are now
required to be defined in the application context
* AbstractBatchConfiguration is now intended to be extended
by users code to get/customize basic infrastructure beans

Issue spring-projects#3942
fmbenhassine added a commit that referenced this issue Sep 20, 2022
These checks occur too early in the bean creation/registration
process and might cause issues when initializing the context.

Related to #3942
@jmresler
Copy link

I know I'm late to the party and I also am a user, not a developer of Spring so please be patient with my ignorance.

A little background, I work for a huge financial services company that uses containers for deployment.
Because containers are dynamic and addresses can change if one goes down and the system restarts, it is difficult to get static external resource access (such as database) approved because they would have to allow secure connections from a conceivably large, dynamic number of addresses and it's hard to get these approved through security.

The issue at hand is that we don't have batch schemas available on our databases because of this dynamic so we predominantly use in memory databases for batch applications. Given multiple data sources are a very common paradigm for batch applications (due to the need to have a batch infrastructure database), would the default use case to provide a data source used by the batch infrastructure not be expected?

The DefaultBatchConfigurer in effect provided a less than robust solution to this problem by using the Map repository. I understand why it was removed but it was very time saving.

Any chance this functionality could be built in to the batch starter?

I am not volunteering my work because I don't expect it to pass code review :-) but I'm writing a basic starter to provide this for some applications I've written. Would be nicer if it was built-in to the API in a robust way.

Just sayin...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants