1

I would like to write an xml file and have the number of processed records and an agregatted total in the header

<?xml version='1.0' encoding='UTF-8'?>
<SettlementReport>
    <GroupHeader>
        <DocumentType>My Report</DocumentType>
        <SettlementInfo>
            <SettlementId>MyReport_20191025</SettlementId>
            <SettlementDate>2019-10-25</SettlementDate>
            <NumOfTransactions>2</NumOfTransactions>
            <ReportTotal>1000</ReportTotal>
        </SettlementInfo>
    </GroupHeader>
    <PaymentInfo>
        <RRN>12345</RRN>
        <VehicleRegistration>AA00AAGP</VehicleRegistration>
        <Province>GP</Province>
        <VPSStatus>Application Submitted to Dept</VPSStatus>
        <SBStatus>Fit for Settlement</SBStatus>
        <ServiceCharge>10.00</ServiceCharge>
        <LicenceFee>402.00</LicenceFee>
        <DeliveryFee>38.00</DeliveryFee>
        <OrderTotal>450.00</OrderTotal>
    </PaymentInfo>
    <PaymentInfo>
        <RRN>12346</RRN>
        <VehicleRegistration>AA00ABGP</VehicleRegistration>
        <Province>GP</Province>
        <VPSStatus>Application Submitted to Dept</VPSStatus>
        <SBStatus>Fit for Settlement</SBStatus>
        <ServiceCharge>10.00</ServiceCharge>
        <LicenceFee>502.00</LicenceFee>
        <DeliveryFee>38.00</DeliveryFee>
        <OrderTotal>550.00</OrderTotal>
    </PaymentInfo>
</SettlementReport>

I am using StaxEventItemWriter and my Beans are configured as follows:

@StepScope
@Bean
StaxEventItemWriter<PaymentInformation> xmlReportWriter() {
    StaxEventItemWriter<PaymentInformation> xmlFileWriter = new StaxEventItemWriter<>();
    String formattedDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
    String exportFilePath = "my_report_".concat(formattedDate).concat(".xml");

    xmlFileWriter.setResource(new FileSystemResource(exportFilePath));
    xmlFileWriter.setRootTagName("SettlementReport");
    xmlFileWriter.setMarshaller(paymentInfoMarshaller());
    xmlFileWriter.setHeaderCallback(getSettlementReportHeaderCallBack());
    return xmlFileWriter;
}

@Bean
@StepScope
SettlementReportHeaderCallBack getSettlementReportHeaderCallBack(){
    return new SettlementReportHeaderCallBack(2, BigDecimal.TEN);
}

@Bean
public Step reportStep() {
    return steps.get("settlementReport")
            .<Order, PaymentInformation>chunk(100)
            .reader(reader())
            .processor(processor())
            .writer(xmlReportWriter())
            .build();
}

My header callback takes two parameters in the constructor because I was hoping to pass the number of transactions and the Total.

@Slf4j
@RequiredArgsConstructor
public class SettlementReportHeaderCallBack implements StaxWriterCallback {
    private final int numberOfTransactions;
    private final BigDecimal controlSum;

    @Override
    public void write(XMLEventWriter writer) {
        XMLEventFactory factory = XMLEventFactory.newInstance();
        try {
            writer.add(factory.createStartElement("", "", "GroupHeader"));

            writer.add(factory.createStartElement("", "", "DocumentType"));
            writer.add(factory.createCharacters("Settlement Report"));
            writer.add(factory.createEndElement("", "", "DocumentType"));

            writer.add(factory.createStartElement("", "", "SettlementInfo"));

            writer.add(factory.createStartElement("", "", "SettlementId"));
            writer.add(factory.createCharacters("VAS_VPS_LD_".concat(LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE))));
            writer.add(factory.createEndElement("", "", "SettlementId"));

            writer.add(factory.createStartElement("", "", "SettlementDate"));
            writer.add(factory.createCharacters(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE)));
            writer.add(factory.createEndElement("", "", "SettlementDate"));

            writer.add(factory.createStartElement("", "", "NumOfTransactions"));
            writer.add(factory.createCharacters(String.valueOf(numberOfTransactions)));
            writer.add(factory.createEndElement("", "", "NumOfTransactions"));

            writer.add(factory.createStartElement("", "", "ReportTotal"));
            writer.add(factory.createCharacters(controlSum.toString()));
            writer.add(factory.createEndElement("", "", "ReportTotal"));

            writer.add(factory.createEndElement("", "", "SettlementInfo"));
            writer.add(factory.createEndElement("", "", "GroupHeader"));
        } catch (XMLStreamException e) {
            log.error("Error writing XML Header: {}", e.getMessage());
            throw new XmlWriterException(e.getMessage());
        }
    }
}

My question is how do I get the number of processed transactions and write them in the header of the same file as the payment infors. Is there a way to access the execution information before writing starts because this cannot be in the footer according to the design I have?

Bright Dodo
  • 531
  • 4
  • 11
  • 1
    Possible duplicate of [Accessing the ExecutionContext values in HeaderCallBack in Spring Batch](https://stackoverflow.com/questions/52076902/accessing-the-executioncontext-values-in-headercallback-in-spring-batch) – Mahmoud Ben Hassine Oct 28 '19 at 09:52
  • @MahmoudBenHassine I have looked at the link above but the answer they suggested does not work for me because the totals cannot be in the footer. Is there a way to make this work in a header and not footer? – Bright Dodo Oct 28 '19 at 10:31
  • 1
    No, since the info is not available at that time. The whole file needs to be read to be able to get the total. And since the step will read/write items in chunks, the file content will be written and it is not possible to re-insert the total in the header afterwards. – Mahmoud Ben Hassine Oct 28 '19 at 11:01
  • @BrightDodo Did you find a way out to achieve what you intended to? – truekiller Jun 09 '21 at 12:42

0 Answers0