在 xml spring 批处理应用程序中复制标题标记 [英] Copy header tag in xml spring batch application

查看:28
本文介绍了在 xml spring 批处理应用程序中复制标题标记的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 spring-boot 应用程序中使用 spring-batch.Spring Boot 版本为 2.3.3.RELEASE.

<块引用>

我想达到的目标

我必须读取一个xml文件,其中包含数以千计的Transactionsheader tag(文件信息).在事务上做一些业务逻辑,然后用事务中更新的值写回文件.我使用 StaxEventItemReader 读取文件,使用 StaxEventItemWriter 写入文件.然后我有几个 ItemProcessors 用于处理业务逻辑.XML 文件看起来像:

<报告文件><文件信息><sender>200GH7XZ60</sender><timestamp>2020-12-23T09:05:34Z</timestamp><环境>PRO</环境><version>001.60</version></文件信息><记录><交易><买家><买家/></交易><交易><买家><买家/></交易><交易><买家><买家/></交易></记录></reportFile>

<块引用>

我面临的问题是标题标签的值.

我已经配置了 OmegaXmlHeaderCallBack,它生成所需的标头标签,但这些标签中的值应该从输入文件中复制.据我所知, StaxWriterCallback 在读取器、处理器和写入器之前被初始化.所以我无法使用 late binding 注入值.这看起来是一个基本要求,但在 stackoverflow 上找不到任何解决方案.

<块引用>

这是配置 spring 批处理作业的代码片段.

@Slf4j@配置@EnableBatchProcessing公共类 BatchConfiguration {@自动连线PIExtractorItemProcessor pIExtractorItemProcessor;@自动连线JobBuilderFactory jobBuilderFactory;@自动连线StepBuilderFactory stepBuilderFactory;@Value( "${eugateway.batch.chunk.size}")私有 int chunkSize;@豆public Step jobStep(ItemStreamReader reader,CompositeItemProcessor处理器,CompositeItemWriter作家,EdsClientItemWriteListener写监听器,StepBuilderFactory stepBuilderFactory) {return stepBuilderFactory.get("extractAndReplacePersonalDataStep").chunk(chunkSize).reader(读者).processor(处理器).listener(writeListener).writer(作家).build();}@豆public Job extractPersonalDataJob(Step jobStep, JobResultListener jobListener,JobBuilderFactory jobBuilderFactory) {return jobBuilderFactory.get("extractAndReplacePersonalDataJob").incrementer(new RunIdIncrementer()).start(jobStep).listener(jobListener).build();}@豆@StepScopepublic ItemStreamReaderitemReader(@Value("#{jobParameters[file.path]}") 字符串路径) {Jaxb2Marshaller transactionMarshaller = new Jaxb2Marshaller();transactionMarshaller.setClassesToBeBound (FileInformation.class, TransactionPositionReport.class);log.info(生成 StaxEventItemReader");返回新的 StaxEventItemReaderBuilder().name("headerTransaction").resource(new FileSystemResource(new FileSystemResource(path))).addFragmentRootElements("fileInformation", "transaction").unmarshaller(transactionMarshaller).build();}@豆@StepScopeOmegaXmlHeaderCallBack getOmegaXmlHeaderCallBack(@Value("#{jobExecutionContext['header.sender']}") 字符串发送者,@Value("#{jobExecutionContext['header.timestamp']}") 字符串时间戳,@Value("#{jobExecutionContext['header.environment']}") 字符串环境,@Value("#{jobExecutionContext['header.version']}") 字符串版本){返回新的 OmegaXmlHeaderCallBack(发件人、时间戳、环境、版本);}@豆@StepScopeOmegaXmlFooterCallBack getOmegaXmlFooterCallBack(){返回新的 OmegaXmlFooterCallBack();}@StepScope@Bean(name = "staxTransactionWriter")public StaxEventItemWriterstaxTransactionItemWriter(@Value("#{jobExecutionContext['header.sender']}") 字符串发送者,@Value("#{jobExecutionContext['header.timestamp']}") 字符串时间戳,@Value("#{jobExecutionContext['header.environment']}") 字符串环境,@Value("#{jobExecutionContext['header.version']}") 字符串版本) {String exportFilePath = "C:\\Users\\saharma\\Documents\\TO_BE_DELETED\\eugateway\\outputfile.xml";资源 exportFileResource = new FileSystemResource(exportFilePath);Jaxb2Marshaller marshaller = new Jaxb2Marshaller();marshaller.setSupportDtd(true);marshaller.setSupportJaxbElementClass(true);marshaller.setClassesToBeBound(TransactionPositionReport.class);返回新的 StaxEventItemWriterBuilder().name("transactionWriter").version(1.0").resource(exportFileResource).marshaller(编组员).rootTagName("reportFile").headerCallback(getOmegaXmlHeaderCallBack(sender, timestamp, environment, version)).footerCallback(getOmegaXmlFooterCallBack()).shouldDeleteIfEmpty(true).build();}@豆@StepScope公共 PIExtractorItemProcessor extractItemProcessor() {log.info(生成 PIExtractorItemProcessor");返回新的 PIExtractorItemProcessor();}@豆公共 PIRemoverItemProcessor removeItemProcessor() {log.info(生成 PIRemoverItemProcessor");返回新的 PIRemoverItemProcessor();}@豆@StepScopeCompositeItemProcessorextractAndRemoveItemProcessor() {log.info(生成 CompositeItemProcessor");CompositeItemProcessoritemProcessor = new CompositeItemProcessor<>();itemProcessor.setDelegates((List>) Arrays.asList(extractItemProcessor(), removeItemProcessor()));返回项目处理器;}@豆@StepScopepublic EdsClientItemWriteredsClientItemWriter() {log.info(生成 EdsClientItemWriter");返回新的 EdsClientItemWriter();}@豆@StepScope公共 OmegaXmlFileWriteromegaXmlFileWriter(@Value("#{jobExecutionContext['header.sender']}") 字符串发送者,@Value("#{jobExecutionContext['header.timestamp']}") 字符串时间戳,@Value("#{jobExecutionContext['header.environment']}") 字符串环境,@Value("#{jobExecutionContext['header.version']}") 字符串版本) {log.info(生成 OmegaXmlFileWriter");返回新的 OmegaXmlFileWriter(staxTransactionItemWriter(sender, timestamp, environment, version));}@豆@StepScopepublic CompositeItemWriterCompositeItemWriter(@Value("#{jobExecutionContext['header.sender']}") 字符串发送者,@Value("#{jobExecutionContext['header.timestamp']}") 字符串时间戳,@Value("#{jobExecutionContext['header.environment']}") 字符串环境,@Value("#{jobExecutionContext['header.version']}") 字符串版本) {log.info(生成 CompositeItemWriter");CompositeItemWriterCompositeItemWriter = new CompositeItemWriter<>();CompositeItemWriter.setDelegates(Arrays.asList(edsClientItemWriter(), omegaXmlFileWriter(sender, timestamp, environment, version)));返回复合项编写器;}}

下面是 OmegaXmlHeaderCallBack 类.由于没有后期绑定,我总是在标题标签中得到空值.

@Slf4j公共类 OmegaXmlHeaderCallBack 实现 StaxWriterCallback {私人字符串发送者;私有字符串时间戳;私有字符串环境;私人字符串版本;public OmegaXmlHeaderCallBack(字符串发送者,字符串时间戳,字符串环境,字符串版本){超级();this.sender = 发件人;this.timestamp = 时间戳;this.environment = 环境;this.version = 版本;}@覆盖public void write(XMLEventWriter writer) {XMLEventFactory factory = XMLEventFactory.newInstance();试试{writer.add(factory.createStartElement("", "", "fileInformation"));writer.add(factory.createStartElement("", "", "sender"));writer.add(factory.createCharacters(sender));writer.add(factory.createEndElement("", "", "sender"));writer.add(factory.createStartElement("", "", "timestamp"));writer.add(factory.createCharacters(timestamp));writer.add(factory.createEndElement("", "", "timestamp"));writer.add(factory.createStartElement(", ", 环境"));writer.add(factory.createCharacters(environment));writer.add(factory.createEndElement(", ", 环境"));writer.add(factory.createStartElement("", "", "version"));writer.add(factory.createCharacters(version));writer.add(factory.createEndElement("", "", "version"));writer.add(factory.createEndElement("", "", "fileInformation"));writer.add(factory.createStartElement("", "", "record"));} catch (XMLStreamException e) {log.error("写入 OMEGA XML 标题时出错:{}", e.getMessage());抛出新的 OmegaXmlHeaderWriterException(e.getMessage());}}}

ItemProcessor 的代码如下.我正在将标头数据设置为 ExecutionContext 以供 headerCallback 读取(遗憾的是不会发生).

@Slf4j公共类 PIExtractorItemProcessor 实现了 ItemProcessor{@自动连线PersonalDataExtractor 个人数据提取器;@Value("#{jobParameters['submission.account']}")私人字符串 subAcntId;@Value("#{stepExecution}")私有 StepExecution stepExecution;@覆盖public ProcessorWriterDto process(CustomHeaderTransactionXmlElement headerTransactionElement) 抛出异常 {文件信息头=空;TransactionPositionReport 交易 = null;if(headerTransactionElement instanceof FileInformation) {header = (FileInformation)headerTransactionElement;stepExecution.getExecutionContext().put(header.sender", header.getSender());stepExecution.getExecutionContext().put(header.timestamp", header.getTimestamp());stepExecution.getExecutionContext().put(header.environment", header.getEnvironment());stepExecution.getExecutionContext().put(header.version", header.getVersion());log.debug("Header {} found.", header.toString());返回空;}其他{交易 = (TransactionPositionReport)headerTransactionElement;log.debug(没有找到交易{}的头信息",transaction.getProcessingDetails().getCustomerTransactionId());log.info(为交易客户 ID {} 提取个人数据并创建 EDS requestDto.", transaction.getProcessingDetails().getCustomerTransactionId());ProcessorWriterDto transferObject = new ProcessorWriterDto();transferObject.setEdsRequestDtoList(personalDataExtractor.extract(transaction, subAcncntId));transferObject.setTransaction(transaction);返回传输对象;}}}

我推荐的链接:

解决方案

你的步骤做得太多了.我会将事情分解为两个步骤:

  • 第一步:提取文件信息头,放入作业执行上下文
  • 第 2 步:从执行上下文中读取文件信息标头,并在该步骤所需的任何步骤范围的 bean 中使用它(例如您的案例中的 stax 回调)

这是一个简单的例子:

import java.io.IOException;导入 java.io.Serializable;导入 java.util.Map;导入 javax.xml.stream.XMLEventWriter;导入 org.springframework.batch.core.Job;导入 org.springframework.batch.core.JobParameters;导入 org.springframework.batch.core.JobParametersBuilder;导入 org.springframework.batch.core.Step;导入 org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;导入 org.springframework.batch.core.configuration.annotation.JobBuilderFactory;导入 org.springframework.batch.core.configuration.annotation.StepBuilderFactory;导入 org.springframework.batch.core.configuration.annotation.StepScope;导入 org.springframework.batch.core.launch.JobLauncher;导入 org.springframework.batch.item.ExecutionContext;导入 org.springframework.batch.item.xml.StaxWriterCallback;导入 org.springframework.batch.repeat.RepeatStatus;导入 org.springframework.beans.factory.annotation.Value;导入 org.springframework.context.ApplicationContext;导入 org.springframework.context.annotation.AnnotationConfigApplicationContext;导入 org.springframework.context.annotation.Bean;导入 org.springframework.context.annotation.Configuration;@配置@EnableBatchProcessing公共类 SO67909123 {@豆公共步骤extractHeaderStep(StepBuilderFactory步骤){return steps.get("extractHeaderStep").tasklet((contribution, chunkContext) -> {映射<字符串,对象>jobParameters = chunkContext.getStepContext().getJobParameters();String inputFile = (String) jobParameters.get("file");文件信息文件信息=提取文件信息(输入文件);ExecutionContext jobExecutionContext = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();jobExecutionContext.put(file.information", fileInformation);返回RepeatStatus.FINISHED;}).build();}私有文件信息提取文件信息(字符串输入文件){//TODO 从 inputFile 中提取标题FileInformation fileInformation = new FileInformation();fileInformation.sender = "200GH7XKDGO3GLZ60";fileInformation.version =001.60";返回文件信息;}@豆公共步骤流程文件(StepBuilderFactory 步骤){return steps.get(processFile").tasklet((contribution, chunkContext) -> {//把它改成一个面向块的 tasklet映射<字符串,对象>jobExecutionContext = chunkContext.getStepContext().getJobExecutionContext();FileInformation fileInformation = (FileInformation) jobExecutionContext.get(file.information");System.out.println("第2步:"+文件信息);返回RepeatStatus.FINISHED;}).build();}@豆@StepScopepublic StaxWriterCallback staxWriterCallback(@Value("#{jobExecutionContext['file.information']}") FileInformation fileInformation) {返回新 StaxWriterCallback() {@覆盖public void write(XMLEventWriter writer) 抛出 IOException {//此处根据需要使用 fileInformation}};}@豆公共作业作业(JobBuilderFactory 作业,StepBuilderFactory 步骤){返回jobs.get(job").start(extractHeaderStep(steps)).next(processFile(steps)).build();}public static void main(String[] args) 抛出异常 {ApplicationContext context = new AnnotationConfigApplicationContext(SO67909123.class);JobLauncher jobLauncher = context.getBean(JobLauncher.class);Job job = context.getBean(Job.class);JobParameters jobParameters = new JobParametersBuilder().addString("file", "transactions.xml").toJobParameters();jobLauncher.run(job, jobParameters);}静态类 FileInformation 实现了 Serializable {私人字符串发送者;私人字符串版本;//其他字段@覆盖公共字符串 toString() {返回文件信息{sender='";+ 发件人 + '\'' + ", version='";+ 版本 + '\'' + '}';}}}

这个例子展示了这个想法.您只需要编写从文件中提取 xml 标记的代码段(仅标题,请参阅 TODO).该示例中的 StaxWriterCallback 是一个 step-scoped bean,可以使用来自执行上下文的标头.步骤 2 中的其他步进范围组件也可以以相同的方式配置(处理器、侦听器等).

I am using spring-batch in spring-boot application. The Spring Boot version is 2.3.3.RELEASE.

What I intend to achieve

I have to read a xml file containing thousands of Transactions with header tag (fileInformation). Do some business logic on transaction and then write the file back with the updated values in transaction. I am using StaxEventItemReader for reading the file and StaxEventItemWriter for writing to the file. Then i have couple of ItemProcessors for handling the business logic. Xml file looks like :

<?xml version="1.0" encoding="UTF-8"?>
<reportFile>
   <fileInformation>
      <sender>200GH7XZ60</sender>
      <timestamp>2020-12-23T09:05:34Z</timestamp>
      <environment>PRO</environment>
      <version>001.60</version>
   </fileInformation>
   <record>
      <transaction>
         <buyer><buyer/>
      </transaction>
      <transaction>
         <buyer><buyer/>
      </transaction>
      <transaction>
         <buyer><buyer/>
      </transaction>
   </record>
</reportFile>

Problem that I am facing is with the value of header tags.

I have configured the OmegaXmlHeaderCallBack which generates the desired header tags but the value in those tags should be copied from the input file. As I am aware the StaxWriterCallback is initialized before reader, processor and writer. So I am not able to inject the value using late binding. This looked like a basic requirement, but couldn't find any solution on stackoverflow.

Here is the code snippets to configure spring batch job.

@Slf4j
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

@Autowired
PIExtractorItemProcessor pIExtractorItemProcessor;

@Autowired
JobBuilderFactory jobBuilderFactory;
 
@Autowired
StepBuilderFactory stepBuilderFactory;

@Value( "${eugateway.batch.chunk.size}" )
private int chunkSize;

@Bean
public Step jobStep(ItemStreamReader<CustomHeaderTransactionXmlElement> reader,
        CompositeItemProcessor<CustomHeaderTransactionXmlElement, 
        ProcessorWriterDto> processor,
        CompositeItemWriter<ProcessorWriterDto> writer,
        EdsClientItemWriteListener<ProcessorWriterDto> writeListener, 
        StepBuilderFactory stepBuilderFactory) {
    return stepBuilderFactory.get("extractAndReplacePersonalDataStep")
            .<CustomHeaderTransactionXmlElement, ProcessorWriterDto>chunk(chunkSize)
            .reader(reader)
            .processor(processor)
            .listener(writeListener)
            .writer(writer)
            .build();
}

@Bean
public Job extractPersonalDataJob(Step jobStep, JobResultListener jobListener,
        JobBuilderFactory jobBuilderFactory) {
    return jobBuilderFactory.get("extractAndReplacePersonalDataJob")
            .incrementer(new RunIdIncrementer())
            .start(jobStep)
            .listener(jobListener)
            .build();
}

@Bean
@StepScope
public ItemStreamReader<CustomHeaderTransactionXmlElement> itemReader(@Value("#{jobParameters[file.path]}") String path) {
    Jaxb2Marshaller transactionMarshaller = new Jaxb2Marshaller();
    transactionMarshaller.setClassesToBeBound (FileInformation.class, TransactionPositionReport.class);
    log.info("Generating StaxEventItemReader");

    return new StaxEventItemReaderBuilder<CustomHeaderTransactionXmlElement>()
            .name("headerTransaction")
            .resource(new FileSystemResource(new FileSystemResource(path)))
            .addFragmentRootElements("fileInformation", "transaction")
            .unmarshaller(transactionMarshaller)
            .build();
}

@Bean
@StepScope
OmegaXmlHeaderCallBack getOmegaXmlHeaderCallBack(@Value("#{jobExecutionContext['header.sender']}") String sender,
        @Value("#{jobExecutionContext['header.timestamp']}") String timestamp,
        @Value("#{jobExecutionContext['header.environment']}") String environment,
        @Value("#{jobExecutionContext['header.version']}") String version){
    return new OmegaXmlHeaderCallBack(sender, timestamp, environment, version);
}

@Bean
@StepScope
OmegaXmlFooterCallBack getOmegaXmlFooterCallBack(){
    return new OmegaXmlFooterCallBack();
}

@StepScope
@Bean(name = "staxTransactionWriter")
public StaxEventItemWriter<TransactionPositionReport> staxTransactionItemWriter(@Value("#{jobExecutionContext['header.sender']}") String sender,
        @Value("#{jobExecutionContext['header.timestamp']}") String timestamp,
        @Value("#{jobExecutionContext['header.environment']}") String environment,
        @Value("#{jobExecutionContext['header.version']}") String version) {
    String exportFilePath = "C:\\Users\\sasharma\\Documents\\TO_BE_DELETED\\eugateway\\outputfile.xml";
    Resource exportFileResource = new FileSystemResource(exportFilePath);

    Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
    marshaller.setSupportDtd(true);
    marshaller.setSupportJaxbElementClass(true);
    marshaller.setClassesToBeBound(TransactionPositionReport.class);

    return new StaxEventItemWriterBuilder<TransactionPositionReport>()
            .name("transactionWriter")
            .version("1.0")
            .resource(exportFileResource)
            .marshaller(marshaller)
            .rootTagName("reportFile")
            .headerCallback(getOmegaXmlHeaderCallBack(sender, timestamp, environment, version))
            .footerCallback(getOmegaXmlFooterCallBack())
            .shouldDeleteIfEmpty(true)
            .build();
}

@Bean
@StepScope
public PIExtractorItemProcessor extractItemProcessor() {
    log.info("Generating PIExtractorItemProcessor");
    return new PIExtractorItemProcessor();
}

@Bean
public PIRemoverItemProcessor removeItemProcessor() {
    log.info("Generating PIRemoverItemProcessor");
    return new PIRemoverItemProcessor();
}

@Bean
@StepScope
CompositeItemProcessor<CustomHeaderTransactionXmlElement, ProcessorWriterDto> extractAndRemoveItemProcessor() {
    log.info("Generating CompositeItemProcessor");
    CompositeItemProcessor<CustomHeaderTransactionXmlElement, ProcessorWriterDto> itemProcessor = new CompositeItemProcessor<>();
    itemProcessor.setDelegates((List<? extends ItemProcessor<?, ?>>) Arrays.asList(extractItemProcessor(), removeItemProcessor()));
    return itemProcessor;
}

@Bean
@StepScope
public EdsClientItemWriter<ProcessorWriterDto> edsClientItemWriter() {
    log.info("Generating EdsClientItemWriter");
    return new EdsClientItemWriter<>();
}

@Bean
@StepScope
public OmegaXmlFileWriter<ProcessorWriterDto> omegaXmlFileWriter(@Value("#{jobExecutionContext['header.sender']}") String sender,
        @Value("#{jobExecutionContext['header.timestamp']}") String timestamp,
        @Value("#{jobExecutionContext['header.environment']}") String environment,
        @Value("#{jobExecutionContext['header.version']}") String version) {
    log.info("Generating OmegaXmlFileWriter");
    return new OmegaXmlFileWriter(staxTransactionItemWriter(sender, timestamp, environment, version));
}


@Bean
@StepScope
public CompositeItemWriter<ProcessorWriterDto> compositeItemWriter(@Value("#{jobExecutionContext['header.sender']}") String sender,
        @Value("#{jobExecutionContext['header.timestamp']}") String timestamp,
        @Value("#{jobExecutionContext['header.environment']}") String environment,
        @Value("#{jobExecutionContext['header.version']}") String version) {
    log.info("Generating CompositeItemWriter");
    CompositeItemWriter<ProcessorWriterDto> compositeItemWriter = new CompositeItemWriter<>();
    compositeItemWriter.setDelegates(Arrays.asList(edsClientItemWriter(), omegaXmlFileWriter(sender, timestamp, environment, version)));
    return compositeItemWriter;
 }  
}

Below is the OmegaXmlHeaderCallBack class. Due to no late binding I always end up getting empty values in header tag.

@Slf4j
public class OmegaXmlHeaderCallBack implements StaxWriterCallback {
    private String sender;
    private String timestamp;
    private String environment;
    private String version;
    
    public OmegaXmlHeaderCallBack(String sender, String timestamp, String environment, String version) {
        super();
        this.sender = sender;
        this.timestamp = timestamp;
        this.environment = environment;
        this.version = version;
    }

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

            writer.add(factory.createStartElement("", "", "sender"));
            writer.add(factory.createCharacters(sender));
            writer.add(factory.createEndElement("", "", "sender"));


            writer.add(factory.createStartElement("", "", "timestamp"));
            writer.add(factory.createCharacters(timestamp));
            writer.add(factory.createEndElement("", "", "timestamp"));

            writer.add(factory.createStartElement("", "", "environment"));
            writer.add(factory.createCharacters(environment));
            writer.add(factory.createEndElement("", "", "environment"));

            writer.add(factory.createStartElement("", "", "version"));
            writer.add(factory.createCharacters(version));
            writer.add(factory.createEndElement("", "", "version"));
            
            writer.add(factory.createEndElement("", "", "fileInformation"));
            
            writer.add(factory.createStartElement("", "", "record"));
            
        } catch (XMLStreamException e) {
            log.error("Error writing OMEGA XML Header: {}", e.getMessage());
            throw new OmegaXmlHeaderWriterException(e.getMessage());
        }
    }
}

Code for ItemProcessor is below. I am setting the header data into ExecutionContext which was intended to be read by headerCallback (sadly not going to happen).

@Slf4j
public class PIExtractorItemProcessor implements ItemProcessor<CustomHeaderTransactionXmlElement, ProcessorWriterDto> {

    @Autowired
    PersonalDataExtractor personalDataExtractor;
    
    @Value("#{jobParameters['submission.account']}") 
    private String subAccntId;
    
    @Value("#{stepExecution}")
    private StepExecution stepExecution;
    
    @Override
    public ProcessorWriterDto process(CustomHeaderTransactionXmlElement headerTransactionElement) throws Exception {
        FileInformation header = null;
        TransactionPositionReport transaction = null;
        if(headerTransactionElement instanceof FileInformation) {
            header = (FileInformation)headerTransactionElement;
            stepExecution.getExecutionContext().put("header.sender", header.getSender());
            stepExecution.getExecutionContext().put("header.timestamp", header.getTimestamp());
            stepExecution.getExecutionContext().put("header.environment", header.getEnvironment());
            stepExecution.getExecutionContext().put("header.version", header.getVersion());
            log.debug("Header {} found.", header.toString());
            return null;
        } else {
            transaction = (TransactionPositionReport)headerTransactionElement;
            log.debug("NO header info found for transaction {}", transaction.getProcessingDetails().getCustomerTransactionId());
            log.info("Extracting personal data for transaction customer id {} and create EDS requestDto.", transaction.getProcessingDetails().getCustomerTransactionId());
            ProcessorWriterDto transferObject = new ProcessorWriterDto();
            transferObject.setEdsRequestDtoList(personalDataExtractor.extract(transaction, subAccntId));
            transferObject.setTransaction(transaction);
            return transferObject;
        }
    }
}

Links that have been referred by me :

解决方案

Your step is doing too much. I would beak things down to two steps:

  • Step 1: extracts the file information header and puts it in the job execution context
  • Step 2: reads the file information header from the execution context and uses it in whatever step-scoped bean needed for that step (for example the stax callback in your case)

Here is a quick example:

import java.io.IOException;
import java.io.Serializable;
import java.util.Map;

import javax.xml.stream.XMLEventWriter;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.xml.StaxWriterCallback;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class SO67909123 {

    @Bean
    public Step extractHeaderStep(StepBuilderFactory steps) {
        return steps.get("extractHeaderStep")
                .tasklet((contribution, chunkContext) -> {
                    Map<String, Object> jobParameters = chunkContext.getStepContext().getJobParameters();
                    String inputFile = (String) jobParameters.get("file");
                    FileInformation fileInformation = extractFileInformation(inputFile);
                    ExecutionContext jobExecutionContext =  chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
                    jobExecutionContext.put("file.information", fileInformation);
                    return RepeatStatus.FINISHED;
                }).build();
    }

    private FileInformation extractFileInformation(String inputFile) {
        // TODO extract header from inputFile
        FileInformation fileInformation = new FileInformation();
        fileInformation.sender = "200GH7XKDGO3GLZ60";
        fileInformation.version = "001.60";
        return fileInformation;
    }

    @Bean
    public Step processFile(StepBuilderFactory steps) {
        return steps.get("processFile")
                .tasklet((contribution, chunkContext) -> { // Change this to a chunk-oriented tasklet
                    Map<String, Object> jobExecutionContext = chunkContext.getStepContext().getJobExecutionContext();
                    FileInformation fileInformation = (FileInformation) jobExecutionContext.get("file.information");
                    System.out.println("Step 2: " + fileInformation);
                    return RepeatStatus.FINISHED;
        }).build();
    }
    
    @Bean
    @StepScope
    public StaxWriterCallback staxWriterCallback(@Value("#{jobExecutionContext['file.information']}") FileInformation fileInformation) {
        return new StaxWriterCallback() {
            @Override
            public void write(XMLEventWriter writer) throws IOException {
                // use fileInformation as needed here 
            }
        };
    }

    @Bean
    public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
        return jobs.get("job")
                .start(extractHeaderStep(steps))
                .next(processFile(steps))
                .build();
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(SO67909123.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        JobParameters jobParameters = new JobParametersBuilder()
                .addString("file", "transactions.xml")
                .toJobParameters();
        jobLauncher.run(job, jobParameters);
    }

    static class FileInformation implements Serializable {
        private String sender;
        private String version;
        // other fields

        @Override
        public String toString() {
            return "FileInformation{sender='" + sender + '\'' + ", version='" + version + '\'' + '}';
        }
    }

}

This example shows the idea. You only need to write the snippet that extracts an xml tag from the file (only the header, see TODO). The StaxWriterCallback in that example is a step-scoped bean and can use the header from the execution context. Other step-scoped components from step 2 can also be configured in the same way (processor, listener, etc).

这篇关于在 xml spring 批处理应用程序中复制标题标记的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆