Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Batch: starting a job from within a Spring MVC contorller WITH A NEW THREAD

I have a Spring-Batch job that I launch from a Spring MVC controller. The controller gets an uploaded file from the user and the job is supposed to process the file:

@RequestMapping(value = "/upload")
public ModelAndView uploadInventory(UploadFile uploadFile, BindingResult bindingResult) {

    // code for saving the uploaded file to disk goes here...

    // now I want to launch the job of reading the file line by line and saving it to the database,
    // but I want to launch this job in a new thread, not in the HTTP request thread,
    // since I so not want the user to wait until the job ends.
    jobLauncher.run(
                    jobRegistry.getJob(JOB_NAME),
                    new JobParametersBuilder().addString("targetDirectory", folderPath).addString("targetFile", fileName).toJobParameters()
                    );

    return mav;
}

I've tried the following XML config:

<job id="writeProductsJob" xmlns="http://www.springframework.org/schema/batch">
    <step id="readWrite">
        <tasklet task-executor="taskExecutor">
            <chunk reader="productItemReader" writer="productItemWriter" commit-interval="10" />
        </tasklet>
    </step>
</job>

<bean id="taskExecutor"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5" />
    <property name="maxPoolSize" value="5" />
</bean>

...but it seems like the multithreading happens only within the job boundaries itself. I.e., the controller thread waits until the job ends, and the job execution is handled by multiple threads (which is good but not the main thing I wanted). The main thing I wanted is that the job will be launched on a separate thread (or threads) while the controller thread will continue its execution without waiting for the job threads to end.

Is there a way to achieve this with Spring-batch?

like image 576
rapt Avatar asked Dec 27 '22 06:12

rapt


2 Answers

The official documentation describes your exact problem and a solution in 4.5.2. Running Jobs from within a Web Container:

[...] The controller launches a Job using a JobLauncher that has been configured to launch asynchronously, which immediately returns a JobExecution. The Job will likely still be running, however, this nonblocking behaviour allows the controller to return immediately, which is required when handling an HttpRequest.

Spring Batch http://static.springsource.org/spring-batch/reference/html-single/images/launch-from-request.png

So you were pretty close in trying to use TaskExecutor, however it needs to be passed to the JobLauncher instead:

<bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
    <property name="taskExecutor" ref="taskExecutor"/>
</bean>

Disclaimer: I have never used Spring Batch...

like image 140
Tomasz Nurkiewicz Avatar answered Dec 31 '22 12:12

Tomasz Nurkiewicz


The jobLauncher.run() method can be called in a new Thread like so:

@RequestMapping(value = "/upload")
public ModelAndView uploadInventory(UploadFile uploadFile, BindingResult bindingResult) {
  [...]

  final SomeObject jobLauncher = [...]
  Thread thread = new Thread(){
    @Override
    public void run(){
      jobLauncher.run([...]);
    }
  };
  thread.start();

  return mav;
}

The thread.start() line will spawn a new thread, and then continue to execute the code below it.

Note that, if jobLauncher is a local variable, it must be declared final in order for it to be used inside of the anonymous Thread class.

like image 31
Michael Avatar answered Dec 31 '22 11:12

Michael