即使步骤完成,使用FlowBuilder的Spring Batch作业也会失败

时间:2018-07-30 23:42:36

标签: spring spring-batch spring-bean

我有一个春季批处理作业,该作业成功完成了步骤,但将整体批处理作业的状态标记为“失败”。这项工作有两个步骤。第一步将验证输入文件并为我们正在处理的文件类型设置一个变量。使用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;
    }

}

0 个答案:

没有答案