简体   繁体   English

如何确保每次执行作业时 MultiResourceItemReader 都会刷新资源

[英]How to make sure the MultiResourceItemReader refreshes the resources each time the job is executed

I have a java application which makes use of spring task scheduling and batch jobs.我有一个使用 spring 任务调度和批处理作业的 java 应用程序。 I am relying on MultiResourceItemReader in the job to read some files from a directory, process them, and remove those files.我靠MultiResourceItemReaderjob从目录中读取一些文件,对其进行处理,并删除这些文件。 An external process is responsible to put new files periodically in that directory.外部进程负责定期将新文件放入该目录中。 But the problem is that each time the job runs, it tries to read the same file resources which were present at the time of launching the application and hence fails because those resources are gone and new files are there now.但问题是,每次job运行时,它都会尝试读取启动应用程序时存在的相同文件资源,因此失败,因为这些资源已经消失,而现在有新文件。

The question is, how do I configure the application so that the resources property is evaluated for every scheduled execution of the given job .问题是,如何配置应用程序,以便为给定job每个计划执行评估资源属性。

The relevant beans are pasted below:相关bean粘贴如下:

  <bean id="multiResourceReader" class="org.springframework.batch.item.file.MultiResourceItemReader">
    <property name="resources" value="file:/opt/data/*.csv" />
    <property name="delegate" ref="testFlatFileItemReader" />
  </bean>

  <batch:job id="MyJob">
    <batch:step id="readandstore">
      <batch:tasklet>
        <batch:chunk reader="multiResourceReader" writer="oracleItemWriter" commit-interval="10" />
      </batch:tasklet>
    </batch:step>
  </batch:job>

  <bean id="runScheduler" class="com.myapp.Scheduler">
    <property name="jobLauncher" ref="jobLauncher" />
    <property name="job" ref="MyJob" />
  </bean>

  <task:scheduled-tasks>
    <task:scheduled ref="runScheduler" method="run" cron="*/30 * * * * *" />
  </task:scheduled-tasks>

I managed to solve the problem I posted myself after having received no response on it for almost a week.我设法解决了我自己发布的问题,将近一个星期没有收到回复。

I removed the resources property of multiResourceReader , and added a StepListener to the listeners under <batch:tasklet> which implements StepExecutionListener .我删除的资源属性multiResourceReader ,并且加入了StepListener下向听众<batch:tasklet>它实现StepExecutionListener This listener has two methods, one is called before the step execution and the other after the step execution.这个监听器有两种方法,一种在步骤执行之前调用,另一个在步骤执行之后调用。 The StepListener accepts a property named filePattern the value for which will be similar to what you'd have provided to resources property of multiResourceReader before. StepListener接受名为filePattern的属性,该属性的值类似于您之前提供给multiResourceReader resources 属性的multiResourceReader Here is how the updated beans look like:以下是更新后的 bean 的样子:

  <batch:job id="MyJob">
    <batch:step id="readandstore">
      <batch:tasklet>
        <batch:chunk reader="multiResourceReader" writer="oracleItemWriter" commit-interval="10" />
        <batch:listeners>
          <batch:listener ref="StepListener" />
        </batch:listeners>
      </batch:tasklet>
    </batch:step>
  </batch:job>

  <bean id="multiResourceReader" class="org.springframework.batch.item.file.MultiResourceItemReader">
    <property name="delegate" ref="csvFileItemReader" />
  </bean>

  <bean id="StepListener" class="com.myapp.StepListener">
    <property name="filePattern" value="file:/opt/data/*.csv" />
  </bean>

And here is how the com.myapp.StepListener looks like:这是 com.myapp.StepListener 的样子:

package com.myapp;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.apache.commons.io.FileUtils;
import org.springframework.batch.item.file.MultiResourceItemReader;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;

public class StepListener implements StepExecutionListener, ApplicationContextAware {

  private static final Logger logger = Logger.getLogger(StepListener.class.getName());
  private Resource[] resources;
  private ApplicationContext applicationContext;
  private String filePattern;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

  public void setFilePattern(String filePattern) {
    this.filePattern = filePattern;
  }

  @Override
  public void beforeStep(StepExecution stepExecution) {

    MultiResourceItemReader reader = (MultiResourceItemReader) applicationContext.getBean("multiResourceReader");
    try {
      resources = applicationContext.getResources(filePattern);
      reader.setResources(resources);
    } catch (IOException ex) {
      logger.log(Level.SEVERE, "Unable to set file resources to bean multiResourceItemReader", ex);
    }
  }

  @Override
  public ExitStatus afterStep(StepExecution stepExecution) {

    if (stepExecution.getExitStatus().equals(ExitStatus.COMPLETED)
            && stepExecution.getStatus().equals(BatchStatus.COMPLETED)
            && resources.length > 0) {

      for (Resource resource : resources) {
        try {
          File oldFile = new File(resource.getFile().getAbsolutePath());
          File newFile = new File(resource.getFile().getAbsolutePath() + ".processed");
          FileUtils.copyFile(oldFile, newFile);
          oldFile.delete();
        } catch (IOException ex) {
          logger.log(Level.SEVERE, "Encountered problem when trying to remove the processed file(s)", ex);
        }
      }
    }

    return stepExecution.getExitStatus();
  }
}

The bean multiResourceReader's scope is singleton, so only 1 will every be created. bean multiResourceReader 的作用域是单例的,所以每个都只会创建 1 个。 When that bean gets created the value of resources is resolved.当那个 bean 被创建时,资源的值就被解析了。

If you set the scope of the bean multiResourceReader to step it should also fix your problem.如果您将 bean multiResourceReader 的范围设置为 step 它也应该可以解决您的问题。 Then a new bean will created every time the step is executed.然后每次执行该步骤时都会创建一个新 bean。

尝试在名为“multiResourceReader”的配置 bean 中添加标签“step”:

Another option is to extend MultiResourceItemReader and override open() method and call setResources() just before calling super.open() method.另一种选择是扩展MultiResourceItemReader并覆盖open()方法并在调用super.open()方法之前调用setResources()

public class CustomMultiItemReader<T> extends MultiResourceItemReader<T> {
    private static final Log logger = LogFactory.getLog(PrestoMultiItemReader.class);
    
    private String filePattern;

    public CustomMultiItemReader() {
        super();
    }

    @Override
    public void open(ExecutionContext executionContext) {
        Assert.notNull(filePattern, "filePattern must be set");
        try {
            ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = patternResolver.getResources(filePattern);
            super.setResources(resources);
        } catch (IOException e) {
            logger.error("No resources to read.", e);
        }
        super.open(executionContext);
    }

    public void setFilePattern(String filePattern) {
        this.filePattern = filePattern;
    }
}

You can call CustomMultiItemReader as below.您可以按如下方式调用CustomMultiItemReader You don't need to call setResources() .您不需要调用setResources()

public CustomMultiItemReader<T> customMultiItemReader() {
    String filePattern = "file:/...";
    CustomMultiItemReader<T> reader = new CustomMultiItemReader<T>();
    reader.setDelegate(reader()); //reader to delegate here
    reader.setFilePattern(filePattern);
    reader.setStrict(true); 
    return reader;
} 

I've face the same problem but I solved with another aproach, maybe could be helpful, I'm working with FlatFileItemReader but the issue is the same.我遇到了同样的问题,但我用另一种方法解决了,也许会有所帮助,我正在使用 FlatFileItemReader 但问题是一样的。

The solution was using a ref in property resource with a bean that handle with jobParams, this way every time step start again it is a new instance with a fresh value, the following code it is how I do it:解决方案是在属性资源中使用 ref 和一个处理 jobParams 的 bean,这样每次重新开始时它都是一个具有新值的新实例,下面的代码就是我这样做的方式:

<bean id="customFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" ref="customFile"/><!-- avoid use directly jobParams here -->
<property name="linesToSkip" value="0" />
<property name="encoding" value="ISO-8859-1" />
<property name="comments" value="*, \u001A" />
<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">
            <bean class="br.com.sample.batch.CustomFieldSetMapperSIAW"/>
        </property>
    </bean>
</property>

<bean id="customFile" class="org.springframework.core.io.FileSystemResource" scope="step">
<constructor-arg value="#{jobParameters[arquivoGSIN]}"></constructor-arg>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM