The client of the JobLauncher is the main application, similar to the CommandLineJobRunner, but with an additional support for interruptions, using non-public sun classes SignalHandler and Signal to register a callback that stops the jobLauncher when the process is interrupted from outside (kill command). So yes, there is another thread, but I do not have to manage it, and the jobLauncher's taskExecutor can still be synchronous.
In BatchLauncher main class, I use something of the form :
Code:
private void listenToOsInterruption() {
...
Signal.handle(someSignal, new SignalHandler() {
public void handle(Signal s) {
jobLauncher.stop();
}
}
...
}
while the main() method of the same class simply invokes jobLauncher.run(). None of the code above is aware of the jobLauncher being synchronous or asynchronous.
When the java program executes, it invokes jobLauncher.run(), and if I kill the process using a registered signal, a thread is started by the JVM in the batch process, that eventually invokes the registered handle() method above, and stops the jobLauncher.
Now, in order to be able to use this functionnality, as stop() is on JobExecution, I must get a handle on it, and therefore I need to return immediately from jobLauncher.run(), which implies that I must use an asynchronous JobLauncher.
This implies that the main application must now wait for the job thread to end before reading and interpreting the status for the jobExecution object, and I don't know how to be notified of that event, so I have to wait for some time, then poll the object, until isRunning() is false.
Code:
public void execute() {
...
// we should immediately return from run()
// store the jobExecution, so that the listenToOsInterruption can find it
this.jobExecution = jobLauncher.run(job, jobParameters);
waitUntilCompleted();
...
}
private void waitUntilCompleted() {
// synchronization/exception omitted for clarity
while (true) {
if (this.jobExecution.isRunning()) {
this.jobExecution.wait(5000);
} else {
return;
}
}
}
private void listenToOsInterruption() {
...
Signal.handle(someSignal, new SignalHandler() {
public void handle(Signal s) {
this.jobExecution.stop();
}
}
...
}
So, I was wondering there is a more elegant way of doing this ? I would be happy if I could configure the JobLauncher to be either synch or asynch without interfering on the the main application.
As I write this post, I notice another issue : if the job runs asynchronously, it might be possible that job.execute(jobExecution) from taskExecutor's Runnable actually starts some times after I receive the JobExecution back to the main application. Therefore, if I invoke stop() on it, since the jobExecution did not start yet (no stepExecution is registered yet), nothing will be done, and when job.execute(jobExecution) eventually starts, the job will be started without being aware it was requested to stop. At least a flag must be set on it, in order to prevent it from starting if stop() is invoked inbetween.
Further, the JobExecution startTime is initialized at instanciation, that is, in a delayed scenario, some times before the taskExecutor's Runnable executes (I initially thought I could use this value to test if we are between creation and execution). This is not correct, as execution time will not reflect the exact time spent during execution.