我有一个春季批处理作业,该作业成功完成了步骤,但将整体批处理作业的状态标记为“失败”。这项工作有两个步骤。第一步将验证输入文件并为我们正在处理的文件类型设置一个变量。使用Tasklet和JobExecutionDecider会发生这种情况。然后,流构建器决定何时运行步骤2的步骤。一切运行正常,每一步都完成了,但是整个批处理状态设置为FAILED而不是COMPLETED。
如果我检查表BATCH_STEP_EXECUTION中的步骤状态(SELECT * FROM BATCH_STEP_EXECUTION WHERE JOB_EXECUTION_ID = 54;),我看到两个步骤都标记为COMPLETED。
您知道为什么我的整体批处理作业失败了吗?
这是返回批处理状态的信息:
2018-07-26 15:21:38.132信息95001 --- [ask-scheduler-1] o.s.b.c.l.support.SimpleJobLauncher:Job:[FlowJob:[name = importEmployees]]使用以下参数完成: [{input.file.name = / Users / aaronradich / Dev / posthire-batch / target / classes / data / import / employees.csv,时间= 1532643668685}]和以下状态:[失败 ]
这是相关的代码。我猜它与FlowBuilder的配置方式有关吗?:
@Bean
protected Step step1(FlatFileItemReader reader, FileValidationTasklet fileValidationTasklet) {
return steps.get("step1").tasklet(fileValidationTasklet)
.build();
}
@Bean
protected Step step2(FlatFileItemReader reader, EmployeeProcessor processor, EmployeeWriter writer) {
return steps.get("step2")
.<Employee, Employee>chunk(10)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
public Job importEmployees(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2) {
JobBuilder jobBuilder = jobBuilderFactory
.get("importEmployees")
.incrementer(new RunIdIncrementer());
StepDecider fileDecider = new StepDecider();
Flow flowFile = new FlowBuilder<Flow>("fileFlow")
.start(fileDecider)
.on(BatchConfig.STATE_DO_FILE_1)
.to(step2)
.on(BatchConfig.STATE_DO_FILE_2)
// TODO: Add an additional Step for File Type 2 (maybe called stepF2, etc.)
.to(step2)
.from(fileDecider)
.on(BatchConfig.STATE_SKIP_FILE)
.end(BatchConfig.STATE_FAILED)
.on("*")
.end(BatchConfig.STATE_COMPLETED)
.build();
/*
* Complete Workflow:
*
* |--> (S2)
* S1 ---> -+
* |--> (S3)
*/
FlowJobBuilder builder = jobBuilder
.flow(step1)
.next(flowFile)
.end();
return builder.build();
}
这是JobExecutionDecider:
package com.sterlingts.posthire.batch;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
import com.sterlingts.posthire.batch.config.BatchConfig;
public class StepDecider implements JobExecutionDecider {
@Override
public FlowExecutionStatus decide(org.springframework.batch.core.JobExecution jobExecution,
org.springframework.batch.core.StepExecution stepExecution) {
String fileTypeCode = jobExecution.getExecutionContext().getString("FileTypeCode");
String returnStatus = "";
switch(fileTypeCode) {
case "1":
returnStatus = BatchConfig.STATE_DO_FILE_1;
break;
case "2":
returnStatus = BatchConfig.STATE_DO_FILE_2;
break;
default:
returnStatus = BatchConfig.STATE_SKIP_FILE;
break;
}
return new FlowExecutionStatus(returnStatus);
}
}
这是TaskLet,它确定文件是否有效,并在JobContext中为我们正在处理的文件类型设置变量:
package com.sterlingts.posthire.batch.util;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
public class FileValidationTasklet implements Tasklet {
private final String FILE_HEADER_1 = "EmployeeId,SSN,FirstName,MiddleName,LastName,Suffix,MaidenName,DOB,Gender,Country,Street,AptNo,City,Region,PostalCode,PhoneDay,PhoneDayExt,PhoneEve,PhoneEveExt,Email,Account,EmployeeStatus,HireDate,StartDate,TerminateDate,DLNumber,DLRegion,LastNameOnLicense,FirstNameOnLicense,MiddleNameOnLicense,JobPosition,BillCode";
private final String FILE_HEADER_2 = "EmployeeId,SSN,FirstName,MiddleName,LastName";
private static final Logger log = LoggerFactory.getLogger(FileValidationTasklet.class);
private String inputFile;
private String fileTypeCode;
public FileValidationTasklet() {
}
public FileValidationTasklet(String inputFile) {
this.inputFile = inputFile;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("Input file is " + inputFile);
if (this.isValidFile()) {
// store the FileTypeCode in the jobExecutionContext so we can access it in other steps
chunkContext
.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext()
.put("FileTypeCode", this.fileTypeCode);
} else {
throw new Exception("File '" + inputFile + "' is not a valid file format! Aborting import!");
}
// set the exit status for the Tasklet to Completed so the overall job can get completed
//chunkContext.getStepContext().getStepExecution().setExitStatus(ExitStatus.COMPLETED);
chunkContext.getStepContext().getStepExecution().setStatus(BatchStatus.COMPLETED);
// tell Batch to continue on to the next step and not repeat this one
return RepeatStatus.FINISHED;
}
private Boolean isValidFile() {
Boolean validFile = false;
BufferedReader brFile;
try {
brFile = new BufferedReader(new FileReader(inputFile));
String headerRow = brFile.readLine();
// have noticed that sometimes the leading character from the readLine is a space
headerRow = headerRow.trim();
// strip CR and LF just in case the file originated from Windows and still has a CR or LF after the readLine
headerRow = headerRow.replaceAll("\r", "").replaceAll("\n", "");
// remove the leading byte-order mark (BOM) character
headerRow = SpringIntegrationUtils.removeUTF8BOM(headerRow);
if (headerRow.equals(FILE_HEADER_1)) {
this.fileTypeCode = "1";
validFile = true;
} else if (headerRow.equals(FILE_HEADER_2)) {
this.fileTypeCode = "2";
validFile = true;
}
} catch (FileNotFoundException e) {
log.error("File '" + inputFile + "' was not found! Aborting!");
} catch (IOException e) {
log.error("Error validating header row of file '" + inputFile + "'!");
}
return validFile;
}
}