我有一个应用程序可以监视FTP文件夹中的特定csv文件foo.csv
,一旦找到该文件,它会将其拉到我的本地文件并生成新的输出格式{ {1}},然后应用程序会将新文件bar.csv
发送回FTP文件夹,并从本地删除它。
现在,我想介绍一个过程,该过程将读取bar.csv
并将其插入数据库表中,然后再次将其发送给FTP服务器。
我假设可以使用Spring Batch Integration来完成此操作,但是我找不到执行该操作的方法。
以下是我的应用程序代码,以供参考和建议。
bar.csv
文件信息发送给Job类
public IntegrationFlow localToFtpFlow(Branch myBranch) {
return IntegrationFlows.from(Files.inboundAdapter(new File(myBranch.getBranchCode()))
.filter(new ChainFileListFilter<File>()
.addFilter(new RegexPatternFileListFilter("final" + myBranch.getBranchCode() + ".csv"))
.addFilter(new FileSystemPersistentAcceptOnceFileListFilter(metadataStore(dataSource), "foo"))),//FileSystemPersistentAcceptOnceFileListFilter
e -> e.poller(Pollers.fixedDelay(10_000)))
.enrichHeaders(h ->h.headerExpression("file_originalFile", "new java.io.File('"+ myBranch.getBranchCode() +"/FEFOexport" + myBranch.getBranchCode() + ".csv')",true))
.transform(p -> {
LOG.info("Sending file " + p + " to FTP branch " + myBranch.getBranchCode());
return p;
})
.log()
.transform(m -> {
this.defaultSessionFactoryLocator.addSessionFactory(myBranch.getBranchCode(),createNewFtpSessionFactory(myBranch));
LOG.info("Adding factory to delegation");
return m;
})
.publishSubscribeChannel(s ->
s.subscribe(f -> f.transform(fileMessageToJobRequest()).handle(jobLaunchingGateway()).channel("nullChannel"))
.subscribe(h -> h.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), FileExistsMode.REPLACE)
.useTemporaryFileName(true)
.autoCreateDirectory(false)
.remoteDirectory(myBranch.getFolderPath()), e -> e.advice(expressionAdvice()))))
/*.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), FileExistsMode.REPLACE)
.useTemporaryFileName(true)
.autoCreateDirectory(false)
.remoteDirectory(myBranch.getFolderPath()), e -> e.advice(expressionAdvice()))*/
.get();
}
@Bean
public FileMessageToJobRequest fileMessageToJobRequest(){
FileMessageToJobRequest fileMessageToJobRequest = new FileMessageToJobRequest();
fileMessageToJobRequest.setFileParameterName("input.file.name");
fileMessageToJobRequest.setJob(orderJob);
return fileMessageToJobRequest;
}
@Bean
public JobLaunchingGateway jobLaunchingGateway() {
SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
//simpleJobLauncher.setJobRepository(jobRepository);
simpleJobLauncher.setTaskExecutor(new SyncTaskExecutor());
JobLaunchingGateway jobLaunchingGateway = new JobLaunchingGateway(simpleJobLauncher);
return jobLaunchingGateway;
}
在运行时添加流时出现错误。
public class FileMessageToJobRequest {
private Job job;
private String fileParameterName;
public void setFileParameterName(String fileParameterName) {
this.fileParameterName = fileParameterName;
}
public void setJob(Job job) {
this.job = job;
}
@Transformer
public JobLaunchRequest toRequest(Message<File> message) {
JobParametersBuilder jobParametersBuilder =
new JobParametersBuilder();
jobParametersBuilder.addString(fileParameterName,
message.getPayload().getAbsolutePath());//message.getPayload().getAbsolutePath()
return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
}
}
答案 0 :(得分:2)
您需要考虑在publishSubscribeChannel()
定义中使用IntegtrationFlow
。将您的.handle(Ftp.outboundAdapter())
作为一个订户放置(最好是第二个)。作为第一个,它应该类似于.handle(jobLaunchingGateway).channel("nullChannel")
。
您可以在春季批注Reference Manual中阅读有关JobLaunchingGateway
的信息。
重点是您要将同一条消息发送到多个地方。因此,PublishSubscribeChannel
是最好的选择:https://docs.spring.io/spring-integration/docs/5.1.6.RELEASE/reference/html/#java-dsl-subflows
我建议您完全按顺序安排那些订户,因为您确实想在发送到FTP之前将其存储到DB中。如果没有executor
,第二个订阅者将等到第一个订阅者完成其工作。
那里.channel("nullChannel")
是必需的,因为JobLaunchingGateway
实际上是网关,并且它返回JobExecution
作为答复。由于您对此不感兴趣,因此只需忽略。当然,在该网关之后,您可能还有另一个handle()
,以某种方式处理此JobExecution
。关键是不要从第一手用户返回任何东西作为答复。它会以某种方式阻止您的主要工作。
更新
我认为.transform(fileMessageToJobRequest())
必须在jobLaunchingGateway
之前转到下面的第一个订阅者:
.publishSubscribeChannel(s ->
s.subscribe(f -> f.transform(fileMessageToJobRequest()).handle(jobLaunchingGateway()).channel("nullChannel"))
.subscribe(h -> h.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), ...))
重点是您想将相同的文件发送到下一个handle()
,但是在上游的转换器之后,它将被更改为JobLaunchRequest
,这对{{1} },但不适用于jobLaunchingGateway
。
您的例外情况:
Ftp.outboundAdapter()
导致这段代码:Caused by: java.lang.NullPointerException
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:98)
。因此,以某种方式Assert.notNull(jobParameters, "The JobParameters must not be null.");
会得出jobParametersBuilder.toJobParameters()
。
我无法提供太多自定义代码。
UPDATE2
好。看来您的问题出在null
bean定义中。您在其中进行了显式的JobLaunchingGateway
,而没有进行new SimpleJobLauncher()
注入。
似乎在Spring Boot的JobRepository
中有一个BasicBatchConfigurer
,可以将其注入到此BatchConfigurerConfiguration
中。因此,我们将通过NPE。此后还有其他错误,但这已经是另外一个故事了……