我有一个简单的控制器,该控制器接受文件路径的JSON字符串并在这些文件上运行spring批处理作业。为了实现Spring Batch,我正在遵循一个教程,该教程最终将在https://github.com/michaelhoffmantech/patient-batch-loader中产生代码。
@RestController
public class JobController {
private static final Log _logger = LogFactory.getLog(JobController.class);
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job job;
@RequestMapping(value = "/job/import", method = RequestMethod.POST)
public void importAsset(@RequestBody String uploadedFiles) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
_logger.debug("importAsset() >>");
try{
_logger.debug("Uploaded files: ");
_logger.debug(uploadedFiles);
_logger.debug("Putting values into the parameter map...");
Map<String, JobParameter> parameterMap = new LinkedHashMap<>();
parameterMap.put(Constants.JOB_PARAM_UPLOADED_FILES, new JobParameter(uploadedFiles));
_logger.debug("Launching job [" + job.getName() + "]...");
jobLauncher.run(job, new JobParameters(parameterMap));
_logger.debug("<< importAsset()");
}
catch (Exception e){
String errorMessage = "An error occured while importing a batch. " + e.getLocalizedMessage();
_logger.error(errorMessage, e);
throw e;
}
}
}
批处理配置为:
@Configuration
@EnableBatchProcessing
public class BatchConfig implements BatchConfigurer {
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private DataSource dataSource;
private JobRepository jobRepository;
private JobExplorer jobExplorer;
private JobLauncher jobLauncher;
@Override
public JobRepository getJobRepository() throws Exception {
return this.jobRepository;
}
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.platformTransactionManager;
}
@Override
public JobLauncher getJobLauncher() throws Exception {
return this.jobLauncher;
}
@Override
public JobExplorer getJobExplorer() throws Exception {
return this.jobExplorer;
}
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(this.jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(this.dataSource);
factory.setTransactionManager(getTransactionManager());
factory.afterPropertiesSet();
return factory.getObject();
}
@PostConstruct
public void afterPropertiesSet() throws Exception{
this.jobRepository = createJobRepository();
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(this.dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
this.jobExplorer = jobExplorerFactoryBean.getObject();
this.jobLauncher = createJobLauncher();
}
}
最后,作业配置是:
@Configuration
public class BatchJobConfig {
private static final Log _logger = LogFactory.getLog(BatchJobConfig.class);
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry){
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}
@Bean
public Step step() throws Exception{
return this.stepBuilderFactory
.get(Constants.STEP_NAME)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
_logger.debug("execute() >>");
_logger.debug("<< execute()");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Job job(Step step){
return this.jobBuilderFactory
.get(Constants.JOB_NAME)
.validator(validator())
.start(step)
.build();
}
public JobParametersValidator validator(){
return new JobParametersValidator(){
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
_logger.debug("validate() >>");
String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
if(StringUtils.isBlank(filePathsJsonStr)){
throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
}
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<UploadFile>>(){}.getType();
ArrayList<UploadFile> uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
for(UploadFile uploadFile: uploadedFiles){
File file = new File(uploadFile.getPath());
if(!file.exists()){
throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
}
}
_logger.debug("<< validate()");
}
};
}
}
当我通过REST方法运行批处理作业时。我在日志中得到以下内容:
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : importAsset() >>
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : Uploaded files:
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : [{"path":"C:\\app-import-staging\\cat-pet-animal-domestic-104827.jpeg","username":"test"},{"path":"C:\\app-import-staging\\kittens-cat-cat-puppy-rush-45170.jpeg","username":"test"}]
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : Putting values into the parameter map...
2018-09-29 20:00:51.681 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController : Launching job [app-import]...
2018-09-29 20:00:51.745 ERROR 10104 --- [nio-8102-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
at java.util.HashMap.putVal(HashMap.java:657) ~[na:1.8.0_181]
at java.util.HashMap.put(HashMap.java:612) ~[na:1.8.0_181]
at java.util.HashSet.add(HashSet.java:220) ~[na:1.8.0_181]
at java.util.Collections$SynchronizedCollection.add(Collections.java:2035) ~[na:1.8.0_181]
at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:508) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
我注意到的是那部分:
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
一直不断,直到引发StackOverflowError。
对于解决此问题的任何建议或帮助,将不胜感激。
答案 0 :(得分:1)
我最终通过删除BatchJobConfig
并修改BatchConfig
类来简化工作,如下所示:
@Configuration
@EnableBatchProcessing
public class BatchConfig {
private static final Log _logger = LogFactory.getLog(BatchConfig.class);
@Bean
public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory){
Step step = stepBuilderFactory.get(Constants.STEP_NAME)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
_logger.debug("execute() >>");
_logger.debug("<< execute()");
return RepeatStatus.FINISHED;
}
})
.build();
return jobBuilderFactory.get(Constants.JOB_NAME)
.incrementer(new RunIdIncrementer())
.validator(validator())
.start(step)
.build();
}
public JobParametersValidator validator(){
return new JobParametersValidator(){
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
_logger.debug("validate() >>");
String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
if(StringUtils.isBlank(filePathsJsonStr)){
throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
}
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<UploadFile>>(){}.getType();
ArrayList<UploadFile> uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
for(UploadFile uploadFile: uploadedFiles){
File file = new File(uploadFile.getPath());
if(!file.exists()){
throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
}
}
_logger.debug("<< validate()");
}
};
}
}
然后,作业按预期执行。
答案 1 :(得分:0)
BatchConfig.java
中的更改
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.platformTransactionManager;
}
到
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return new DataSourceTransactionManager(dataSource);
}