Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

java - Accessing the ExecutionContext values in HeaderCallBack in Spring Batch

I'm trying to write a spring batch job which reads data from database and writes to FlatFile. I need to write the total records, total lines in the header of the FlatFile. Using the headerCallback property in Writer for that purpose.

Problem statement:

I'm setting the values in ExecutionContext in Processor and trying to retrieve the values in headerCallback of the Writer. But getting the values as null in headerCallback.

Tried using ExecutionContextPromotionListener and also by injecting the values directly into headerCallback using SpEL. Details are as below:

Job configuration:

<batch:job id="fooBatchJob">
<batch:step id="step1">
        <batch:tasklet>
            <batch:chunk reader="fooJdbcItemReader" writer="fooFileItemWriter" processor="fooProcessor"
                commit-interval="100">
            </batch:chunk>
        </batch:tasklet>
        <batch:listeners>
            <batch:listener ref="execContextPromotionListener"/>
        </batch:listeners>
    </batch:step>
</batch:job>

<!-- Listeners -->

<bean id="execContextPromotionListener" class="org.springframework.batch.core.listener.ExecutionContextPromotionListener">
    <property name="strict" value="TRUE"/>
    <property name="keys">
        <list>
            <value>TOTAL_RECORDS</value>
            <value>TOTAL_LINES</value>
        </list>
    </property>
</bean>

The values of TOTAL_RECORDS and TOTAL_LINES are populated with the values from the query in ItemReader.

I'm setting the values in Processor as below:

public class FooProcessor implements ItemProcessor<MyObj,MyObj>, StepExecutionListener {

    private StepExecution stepExecution;

    public MyObj process(MyObj item) throws Exception {

        if(item != null && item.getRecordType().equals("HEADER")) {

            this.stepExecution.getExecutionContext().put("TOTAL_RECORDS", item.getMetaData().getTotalRecordsCount());
            this.stepExecution.getExecutionContext().put("TOTAL_LINES", item.getMetaData().getTotalLines());
            // below lines printed for debugging:
            System.out.println("TOTAL_RECORDS:"+this.stepExecution.getExecutionContext().getInt("TOTAL_RECORDS"));
            System.out.println("TOTAL_LINES:"+this.stepExecution.getExecutionContext().getInt("TOTAL_LINES"));
            return null;
        }
        return item;
    }

    @Override
    public void beforeStep(StepExecution stepExecution) {
        this.stepExecution = stepExecution;

    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        return null;
    }
}

Now, here is the Writer configuration:

<bean id="fooFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
        <property name="resource" value="file:output/result-file.asc"/>
        <property name="shouldDeleteIfExists" value="true" />
        <property name="headerCallback" ref="myHeaderFooter"/>
        <property name="footerCallback" ref="myHeaderFooter"/>
        <property name="lineAggregator">
            <bean
                class="com.domain.batch.transform.FooLineAggregator">
            </bean>
        </property>
    </bean> 

<!-- Header and Footer Callback -->
    <bean id="myHeaderFooter" class="com.domain.batch.transform.MyHeaderFooter" scope="step">
        <property name="stepExecution" value="#{stepExecution}" />
        <property name="totalRecords" value="#{jobExecutionContext['TOTAL_RECORDS']}"/>
    </bean> 

Below is the listing of MyHeaderFooter.java:

public class MyHeaderFooter implements FlatFileHeaderCallback, FlatFileFooterCallback {

    private StepExecution stepExecution;
    private Integer totalRecords;

    private static final String ITEM_END_INDICATOR = "RE";
    private static final String SPACE_DELIMITER = " ";
    private static final String LINE_SEPARATOR = System.lineSeparator();

    @Override
    public void writeHeader(Writer writer) throws IOException {
        // printing out null
        System.out.println("Total records from jobExecContext : "+totalRecords); 
        ExecutionContext executionContext = stepExecution.getJobExecution().getExecutionContext();
        // printing out null
        System.out.println("Total Records :"+ executionContext.get("TOTAL_RECORDS"));
       // printing out null
        System.out.println("Total Lines:"+ executionContext.get("TOTAL_LINES"));
        // Below lines commented out for brevity
        //String header = buildHeader(...);
        //writer.write(header);
    }

    @Override
    public void writeFooter(Writer writer) throws IOException {
        String footer = "footer data....";
        writer.write(footer);
    }

public void setStepExecution(StepExecution stepExecution) {
        this.stepExecution = stepExecution;
    }

    public void setTotalRecords(Integer totalRecords) {
        this.totalRecords = totalRecords;
    }
}

Not sure what i'm doing wrong with the configuration. Any clue to find out the issue would be helpful.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The writeHeader method is called while opening the fooFileItemWriter. Reading, processing and writing have not started yet at that point in time. So the information you are requesting is not available yet.

Note that it would work if you want to write these information in a footer line.

There is a similar question to this one, I'm adding it here for reference: Passing variables to process.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...