How To Propagate Context Information Throw Spring Batch
Introduction to a framework that performs the propagation of Spring Security, Sleuth, MDC, and Locale (Internationalization) contexts inside Spring Batch items.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
While developing applications using Spring batch, especially in a micro-service project, we sometimes face one or most of the following cases:
- The necessity of getting the security context inside the batch items to call methods that require authorizations inside the same micro-service or perform remote processing by calling other micro-services using Feign Client (HTTP) or Spring Cloud Stream (broker like Kafka, RabbitMq ...)
- Propagating Sleuth trace Id and span Id in order to enhance logs traceability inside all the application components including other micro-services so the trace will not be lost if we use Job.
- Getting the connected user Locale (i18n) in order to generate internationalized output otherwise, all the Job outputs will be generated in the default server language.
- Retrieving objects stored inside Mapped Diagnostic Context (MDC) for tracing purposes.
The following schema illustrates remote calls that can be performed in a micro-service-based application and the context information that String Batch items can propagate.
The cases described above can be resolved by passing the context information as job parameters and restoring them before the job or step runs using JobExecutionListener
or StepExecutionListener
, respectively, according to the execution configuration made (One thread per job or thread pool that).
The problem with this approach is that the code responsible for the job parameters injection will appear with the business one which can cause confusion during code maintenance, also this processing should be transparent for the developers so they can concentrate more on the business logic. This is why I want to introduce a new framework that can be used in order to propagate the contexts described above with the possibility of adding any other information easily.
How It Works
The following diagram describes roughly how this framework works:
- An aspect that intercepts the call of
JobLauncher.run()
method in order to insert the context information as a job parameter. - A customized
JobExecutionListener
is called by Spring Batch before the start of the Job in order to deserialize the context parameters and put them in theExecutionContext
. - A customized
StepExecutionListener
is called by Spring Batch before the start of the step in order to restore the context and clear it and the end of the step.
The Job JobExecutionListener
and StepExecutionListener
are injected and associated automatically with any job by the framework. So no need for extra code.
How To Use It
It's very simple; since the framework is provided with starters, simply add the dependency that corresponds to the needed context.
For Spring Security:
<dependency>
<groupId>org.digibooster.spring.batch</groupId>
<artifactId>spring-batch-security</artifactId>
<version>1.0.0</version>
</dependency>
For Sleuth:
xxxxxxxxxx
<dependency>
<groupId>org.digibooster.spring.batch</groupId>
<artifactId>spring-batch-sleuth</artifactId>
<version>1.0.0</version>
</dependency>
For MDC:
xxxxxxxxxx
<dependency>
<groupId>org.digibooster.spring.batch</groupId>
<artifactId>spring-batch-mdc</artifactId>
<version>1.0.0</version>
</dependency>
For Locale (i18n):
x
<dependency>
<groupId>org.digibooster.spring.batch</groupId>
<artifactId>spring-batch-locale</artifactId>
<version>1.0.0</version>
</dependency>
Advanced Use
In order to propagate a customized context, the developer needs to implement the interface JobExecutionContextListener
and inject it as a Spring bean. The new bean will be considered automatically by the framework.
x
/**
* Allow the restoring the context of the thread that runs the job inside the
* job it self.
*
*/
public interface JobExecutionContextListener {
/**
* Serializes the current context information and puts it in the the job
* parameter. This method called by the Aspect {@link JobExecutionAspect}
*
* @param jobParametersBuilder
*/
void insertContextInfo(JobParametersBuilder jobParametersBuilder);
/**
* Deserializes the context information from the job parameters and inserts it
* in the Job execution context. This method is called by the Job listener
* {@link JobExecutionListenerContextSupport}
*
* @param jobExecution
*/
void fillJobExecutionContext(JobExecution jobExecution);
/**
* Removes the context information from job execution context when the job ends
* This method is called by the Job listener
* {@link JobExecutionListenerContextSupport}
*
* @param jobExecution
*/
void removeFromJobExecutionContext(JobExecution jobExecution);
/**
* Restore the context information from the job execution context before each
* step This method is called by the Step listener
* {@link StepExecutionListenerContextSupport}
*
* @param stepExecution
*/
void restoreContext(StepExecution stepExecution);
/**
* Remove the context information when the step ends This method is called by
* the Step listener {@link StepExecutionListenerContextSupport}
*
* @param stepExecution
*/
void clearContext(StepExecution stepExecution);
}
The framework source code is published in github
Opinions expressed by DZone contributors are their own.
Comments