Josh
Josh

Reputation: 371

Return Spring Batch Job ID before the job completes

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

Answers (1)

Mahmoud Ben Hassine
Mahmoud Ben Hassine

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

Related Questions