Spring Batch作业在重新启动时抛出ItemStreamException;有一个跳过记录的处理器,跳过侦听器写入一个平面文件

时间:2014-07-07 16:03:51

标签: spring-batch skip

我可以帮助你解决我个人非常简单的错误。

我有一个非常基本的测试Spring Batch作业。它利用FlatFileItemReader从文件中读取并使用FlatFileItemWriter进行写入。如果项目的id以a结尾,则自定义处理器会抛出自定义SkipObjectException。 skip侦听器捕获SkipObjectException并使用FlatFileItemWriter将消息(字符串)写入文件。

重新启动时,取决于它何时失败,我得到一个ItemStreamException:当前文件大小小于上次提交时的大小。跳过文件发生此异常。如果我不在skip listener中写入文件,一切正常。

输入文件内容 001;第一条记录 002;第二记录 003;第三条记录 003a;首先跳过记录 004;第四条记录 005;第五条记录 006;第六条记录 007;第七条记录 007a;第二次跳过记录 008;第八条记录 009;第九条记录 010;第十条记录 011;第十一条记录 012;第十二条记录

成功测试 工作顺利。它将12条记录(除了003a和007a之外的所有记录)写入输出文件,并将两条记录(003a和007a)写入跳过的文件。大。

失败测试1 - 提交间隔为5并且处理器在项目004上失败。正如预期的那样,作业失败,输出文件和跳过文件都为空;这是有意义的,因为异常发生在提交间隔之前。

重新启动失败测试1 - 与上述相同但从处理器中删除异常。重新启动作业,它按预期运行,输出与成功测试相同。

失败测试2 - commit-interval为5;使项目004处理器失败。与以前相同的结果。

重新启动失败测试2 - commit-interval为5;使项目008上的处理器失败。作业重新启动然后在预期的位置失败。输出文件有4条记录(001到004除了003a),跳过文件有一条记录003a。因此,输出符合预期。

重新启动失败测试2 - commit-interval为5;从处理器中删除异常。除了ItemStreamException之外,作业无法重新启动:当前文件大小小于上次提交时的大小。跳过文件会发生此异常。

故障测试3 - 将commit-interval设置为10并使处理器在项目012上失败。作业在最后一项上按预期失败。除003a和007a外,输出文件包含项目001到008。跳过文件包含003a和007a。一切都好。

重新启动失败测试3 - 将commit-interval设置为10并从处理器中删除异常。当我重新启动作业时,我得到ItemStreamException:当前文件大小小于上次提交时的大小。这发生在跳过输出文件中。

调试信息 我在调试中执行失败测试3。在重新启动之后,在跳过文件的FlatFileItemWriter中,它认为当应该只有2时,会写入8条记录。

我尝试使用注释实现SkipListener,扩展SkipListenerSupport,实现ItemStream,所有这些都具有相同的结果。是的,我可以使用记录器(例如log4j)来编写它,但我想弄清楚我在实现FlatFileItemWriter时做错了什么。

就像我之前所说的那样,这很简单,但是下面是代码的重要部分 Job.xml     

    <batch:step id="step1">
        <batch:tasklet allow-start-if-complete="false">
            <batch:chunk reader="fileReader" processor="fileProcessor"
                writer="fileWriter" commit-interval="5" skip-limit="10">
                <batch:skippable-exception-classes>
                    <batch:include
                        class="com.myexception.SkipObjectException" />
                </batch:skippable-exception-classes>
                <batch:streams>
                    <batch:stream ref="skipListenerWriter" />
                </batch:streams>
            </batch:chunk>
            <batch:listeners>
                <batch:listener ref="customSkipListener" />
            </batch:listeners>
        </batch:tasklet>
    </batch:step>
</batch:job>

<bean id="fileReader" class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="file:TestFiles/input/input.txt" />
    <property name="lineMapper">
        <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
            <property name="lineTokenizer">
                <bean
                    class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                    <property name="delimiter" value=";" />
                </bean>
            </property>
            <property name="fieldSetMapper" ref="fileMapper" />
        </bean>
    </property>
</bean>

<bean id="fileWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
    <property name="resource" value="file:TestFiles/output/output.txt" />
    <property name="shouldDeleteIfExists" value="true" />
    <property name="lineAggregator">
        <bean
            class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
    </property>
</bean>

<bean id="skipListenerWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
    <property name="resource" value="file:TestFiles/output/skipListener.txt" />
    <property name="shouldDeleteIfExists" value="true" />
    <property name="lineAggregator">
        <bean
            class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
    </property>
</bean>

处理器 - 如果正在处理的项目的id以a结尾,则抛出SkipObjectException。

CustomSkipListener

@Autowired
private FlatFileItemWriter<String> skipListenerWriter;

@OnSkipInProcess
public void processLog(FileObject theItem, Throwable theException) {
    String myMessage = "Process skipped item: " + theException.getMessage()
            + " item: " + theItem.getNumber();

    List<String> theItems = new ArrayList<String>();
    theItems.add(myMessage);
    logger.fatal(myMessage);
    try {
        this.skipListenerWriter.write(theItems);
    } catch (Exception e) {
        // TODO wouldn't actually do this...
        throw new RuntimeException(e);
    }
}

1 个答案:

答案 0 :(得分:2)

我的解决方案,无论是对还是错,都是使用FlatFileItemWriter扩展的ItemStreamSupport抽象类中的name属性来唯一地命名编写器。

Spring Batch将写入文件的数量存储在name.written键下的执行上下文中。 FlatFileItemWriter的默认名称是FlatFileItemWriter。由于我有两个FlatFileItemWriters,因此只有一个项写入执行上下文(FlatFileItemWriter.written)。由于一个文件写入的文件多于重新启动时发生的其他损坏。唯一命名的作者解决了这个问题。如果有更合适的方法来处理这个问题,我将不胜感激。