我有一个Spring批处理作业,它成功完成了这些步骤,但将整个批处理作业状态标记为Failed . 这项工作有两个步骤 . 第一步验证输入文件并为我们正在处理的文件类型设置变量 . 使用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 INFO 95001 --- [ask-scheduler-1] osbclsupport.SimpleJobLauncher:Job:[FlowJob:[name = importEmployees]]使用以下参数完成:[{input.file . name = / Users / aaronradich / Dev / posthire-batch / target / classes / data / import / employees.csv,time = 1532643668685}]及以下状态:[ FAILED ]
这是相关的代码 . 我猜它与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")
.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("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;
}
}