Reputation: 371
I'm trying to return a Spring Batch Job ID before the job completes.
My current implementation only returns the information once the job completes.
I use a batch program controller and a batch service, posted below. Thanks, I'm new to Spring Batch and after exhaustive searching couldn't find much related to my question. There was one post with someone using Apache Camel but I am not.
Controller
@RequestMapping(value = "/batch/{progName}", method = RequestMethod.GET)
public ResponseEntity<JobResults> process(@PathVariable("progName") String progName,
@RequestHeader("Accept") MediaType accept) throws Exception {
HttpHeaders responseHeaders = MccControllerUtils.createCacheDisabledHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_XML);
if(!accept.toString().equals("*/*")) {
responseHeaders.setContentType(accept);
}
LOGGER.info("Running batch program " + progName);
JobResults response = batchService.processProgName(progName);
return new ResponseEntity<JobResults>(response, responseHeaders, HttpStatus.OK);
}
Service
@Override
public JobResults processProgName(String progName) throws Exception {
String jobName = "call" + progName.toUpperCase() + "Job";
JobExecution jobExecution = null;
String result = "";
Long jobId = null;
JobResults results = new JobResults();
BatchStatus jobStatus = null;
try {
// Launch the appropriate batch job.
Map<String, Job> jobs = applicationContext.getBeansOfType(Job.class);
LOGGER.info("BATCHSTART:Starting sync batch job:" + jobName);
JobParametersBuilder builder = new JobParametersBuilder();
// Pass in the runtime to ensure a fresh execution.
builder.addDate("Runtime", new Date());
jobExecution = jobLauncher.run(jobs.get(jobName), builder.toJobParameters());
jobId = jobExecution.getId();
jobStatus = jobExecution.getStatus();
results.setName(jobName);
results.setId(jobId);
results.setMessage("The job has finished.");
results.setStatus(jobStatus);
LOGGER.info("The job ID is " + jobId);
LOGGER.info("BATCHEND:Completed sync batch job:" + jobName);
LOGGER.info("Completion status for batch job " + jobName + " is " + jobExecution.getStatus().name());
List<Throwable> failures = jobExecution.getAllFailureExceptions();
if (failures.isEmpty()) {
result = jobExecution.getExecutionContext().getString(AbstractSetupTasklet.BATCH_PROGRAM_RESULT);
} else {
for (Throwable fail : failures) {
result += fail.getMessage() + "\n";
}
}
LOGGER.info("The job results are: " + result);
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException e) {
throw new RuntimeException("An error occurred while attempting to execute a job. " + e.getMessage(), e);
}
return results;
}
Thanks again.
EDIT
I've added this to my batch configuration
@Bean(name = "AsyncJobLauncher")
public JobLauncher simpleJobLauncher(JobRepository jobRepository){
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
return jobLauncher;
}
EDIT
I solved the problem with the help of Mahmoud Ben Hassine's comment :)
I had to remove the result variable and information from the Service, as well as add the code seen above. When I hit my endpoint now I receive the job information immediately.
Upvotes: 1
Views: 3153
Reputation: 31600
What you can do is to inject a JobLauncher
implementation in your BatchService
that is backed by an asynchronous task executor (for example SimpleAsyncTaskExecutor
or ThreadPoolTaskExecutor
). This will run your jobs asynchronously and will immediately return a job execution (from which you can get the id) without waiting for the job to complete.
So by injecting the right implementation of JobLauncher
in your BatchService
, you will be able to achieve your requirement without changing the code of the processProgName
method.
You can find more details about how to run jobs synchronously or asynchronously here: https://docs.spring.io/spring-batch/4.0.x/reference/html/job.html#configuringJobLauncher
Upvotes: 1