I have a following Spring Batch Job config:
@Configuration @EnableBatchProcessing public class JobConfig { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Bean public Job job() { return jobBuilderFactory.get("job") .flow(stepA()).on("FAILED").to(stepC()) .from(stepA()).on("*").to(stepB()).next(stepC()) .end().build(); } @Bean public Step stepA() { return stepBuilderFactory.get("stepA").tasklet(new RandomFailTasket("stepA")).build(); } @Bean public Step stepB() { return stepBuilderFactory.get("stepB").tasklet(new PrintTextTasklet("stepB")).build(); } @Bean public Step stepC() { return stepBuilderFactory.get("stepC").tasklet(new PrintTextTasklet("stepC")).build(); } }
I’m starting the job with a following code:
try { Map<String,JobParameter> parameters = new HashMap<>(); JobParameter ccReportIdParameter = new JobParameter("03061980"); parameters.put("ccReportId", ccReportIdParameter); jobLauncher.run(job, new JobParameters(parameters)); } catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) { e.printStackTrace(); }
This is my test tasklet:
public class PrintTextTasklet implements Tasklet { public PrintTextTasklet() { } public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception { String ccReportId = chunkContext.getStepContext().getStepExecution().getJobParameters().getString("ccReportId"); System.out.println("ccReportId: " + ccReportId); Thread.sleep(60 * 1000); return RepeatStatus.FINISHED; } }
I use H2 database as a persistent storage for my jobs.
During the jobs execution I’m terminating my application. Right after application restart I expect that all uncompleted jobs will continue execution from terminated steps but nothing happens.
Also, in my application.properties file I added a following property:
spring.batch.job.enabled=false
because I don’t want to allow Spring Batch automatically start new(not terminated) job. I need to start all new jobs manually(on user request) and restart all completed jobs after next application run.
How to configure Spring Batch in this case ?
UPDATED
Right now I’m trying to restart jobs with a following method:
public void restartUncompletedJobs() { List<String> jobs = jobExplorer.getJobNames(); for (String job : jobs) { Set<JobExecution> runningJobs = jobExplorer.findRunningJobExecutions(job); for (JobExecution runningJob : runningJobs) { try { jobOperator.restart(runningJob.getId()); logger.info("Restarted: " + runningJob); } catch (Exception e) { logger.error(e.getMessage(), e); } } } }
but failing with a following exception:
org.springframework.batch.core.launch.NoSuchJobException: No job configuration with the name [job] was registered at org.springframework.batch.core.configuration.support.MapJobRegistry.getJob(MapJobRegistry.java:66) ~[spring-batch-core-3.0.5.RELEASE.jar:3.0.5.RELEASE] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_60] at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_60] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.5.RELEASE.jar:3.0.5.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at com.sun.proxy.$Proxy94.getJob(Unknown Source) ~[na:na] at org.springframework.batch.core.launch.support.SimpleJobOperator.restart(SimpleJobOperator.java:275) ~[spring-batch-core-3.0.5.RELEASE.jar:3.0.5.RELEASE] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_60] at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_60] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:201) ~[spring-aop-4.2.0.RELEASE.jar:4.2.0.RELEASE] at com.sun.proxy.$Proxy96.restart(Unknown Source) ~[na:na] at com.example.domain.api.batch.job.ReportJobServiceImpl.restartUncompletedJobs(ReportJobServiceImpl.java:72) ~[classes/:na] at com.example.domain.api.Application.lambda$0(Application.java:46) [classes/:na] at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:672) ~[spring-boot-1.2.5.RELEASE.jar:1.2.5.RELEASE] at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:690) ~[spring-boot-1.2.5.RELEASE.jar:1.2.5.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) ~[spring-boot-1.2.5.RELEASE.jar:1.2.5.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:957) ~[spring-boot-1.2.5.RELEASE.jar:1.2.5.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:946) ~[spring-boot-1.2.5.RELEASE.jar:1.2.5.RELEASE] at com.example.domain.api.Application.main(Application.java:53) [classes/:na]
Advertisement
Answer
I got it working by adding following line
jobRegistry.register(new ReferenceJobFactory(job));
into the restartUncompletedJobs
method
public void restartUncompletedJobs() { try { jobRegistry.register(new ReferenceJobFactory(job)); List<String> jobs = jobExplorer.getJobNames(); for (String job : jobs) { Set<JobExecution> runningJobs = jobExplorer.findRunningJobExecutions(job); for (JobExecution runningJob : runningJobs) { runningJob.setStatus(BatchStatus.FAILED); runningJob.setEndTime(new Date()); jobRepository.update(runningJob); jobOperator.restart(runningJob.getId()); logger.info("Restarted: " + runningJob); } } } catch (Exception e) { logger.error(e.getMessage(), e); } }