我创建了一个基于 Spring Batch 的应用程序,它基本上有一个自定义 itemreader 用于读取文件夹以搜索候选文件,一个自定义 itemprocessor 用于读取文件并将其转换为不同的文件,以及一个自定义 itemwriter 以删除文件。在我添加调度程序之前,它按预期工作,没有错误。添加调度程序后,我将错误粘贴在问题主题中。我发现了一些具有相似问题但背景非常不同的问题。必须奇怪的一点是,事实上,即使我在控制台上看到这样的错误,它也能正常工作。我的意思是,读取文件夹(itemreader),转换文件(itemprocessor)并删除输入文件(itemwriter)。我很困扰。如果我可以在没有调度程序的情况下重现错误,然后我可以专注于某个节点,或者在我引入调度程序后我没有得到结果,所以我可以专注于调度程序配置中的一些可能的错误,但两者都不是这样。任何建议将不胜感激。
下一段是在 1 月 10 日添加的:经过深入调查,我发现这种错误只发生一次,而且只发生在主线程中。我的意思是,如果我在开始申请时有一个候选文件,那么我会得到 WRONG_DOCUMENT_ERR。比方说,我启动我的应用程序时没有可供 CustomItemReader 找到的文件,等到主线程完成,然后我放置一个可供 CustomItemReader 找到的候选文件,根本不会有错误。换句话说,当 CustomItemReader 被 pool-2-thread-1 触发并找到一个文件时,它可以正常工作。另一方面,当主线程触发 CustomItemReader 并找到一个文件时,这仅在应用程序启动期间发生,它会导致问题。通过使用 JConsole,我可以检查主线程是否已消失,并且 pool-2-thread-1 是否已启动并正在运行。然后,我将我的文件添加到输入文件夹中,这会导致 CustomItemReader 返回一个字符串而不是 null 并且不会出现错误。当然,我错过了一些关于一起使用 Spring Scheduler 和 Spring Batch 的概念。显然,我希望我的应用程序在我启动它时不会引发任何错误,并且它很快就会找到它初始化的文件。为什么它只发生在主线程中,但如果我把 Spring Scheduler 拿出来,它会按预期工作一次?我错过了一些同步参数吗?为什么它只发生在主线程中,但如果我把 Spring Scheduler 拿出来,它会按预期工作一次?我错过了一些同步参数吗?为什么它只发生在主线程中,但如果我把 Spring Scheduler 拿出来,它会按预期工作一次?我错过了一些同步参数吗?
下一段是在 1 月 11 日添加的:我只用更大的片段更新了发生错误的类。其他所有保持不变。
@Component
public class Queue {
private Node queue;
@Autowired
private Environment env;
public Element myMethod(String file) {
//...
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document d = docBuilder.parse(env.getProperty("certainFile.xml"));
Element template = d.getDocumentElement();
queue = d.createElement("c:file");
((Element) queue).setAttribute("xmlns:c", "myApp");
queue.appendChild(queue.getOwnerDocument().importNode(template, true));
template = (Element) queue.getLastChild();
addField(template, "someFieldLabel");
}
private void addField(Element message, String field) {
// ....
Element newField = queue.getOwnerDocument().createElement(field);
for (int k = 0; k < certainList.getLength(); k++) {
if ("...certain logic") {
newField = (Element) queue.getOwnerDocument().importNode(fieldFormat, true);
if ("...other logic"){
newField.setAttribute("manual", "true");
}
newField.removeAttribute("indicator");
break;
}
}
for (int j = 0; j < fields.getLength(); j++) {
Element e = (Element) (fields.item(j));
if (e.getNodeName().equals(fieldType)) {
// some cascades "ifs"
// the error happens in next line but only in the main thread
message.insertBefore(newField, e);
// let's ignore the rest
}
}
}
//----
@Configuration
@ComponentScan("com.example")
@EnableBatchProcessing
@EnableAutoConfiguration
@EnableScheduling
@PropertySource(value="classpath:config.properties")
@PropertySource(value="classpath:ipm.properties",ignoreResourceNotFound=true)
public class BatchConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Step step1(ItemReader<String> reader, ItemProcessor<String, String> processor, ItemWriter<String> writer) {
return stepBuilderFactory.get("step1").<String, String> chunk(1)
.reader(reader).processor(processor).writer(writer).allowStartIfComplete(true)
.build();
}
@Bean
public ItemProcessor<String , String> processor(){
return new CustomItemProcessor();
}
@Bean
public ItemWriter<String> writer() {
return new CustomItemWriter();
}
@Bean
public Job job(Step step1) throws Exception {
return jobBuilderFactory.get("job1")
.incrementer(new RunIdIncrementer()).start(step1).build();
}
@Bean
@StepScope
public ItemReader<String> reader() {
return new CustomItemReader();
}
}
//-----
public class CustomItemReader implements ItemReader<String> {
private static final Logger log = LoggerFactory
.getLogger(CustomItemReader.class);
@Autowired
private Environment env;
@Override
public String read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
String[] stringArray;
try (Stream<Path> stream = Files.list(Paths.get(env
.getProperty("myFile")))) {
stringArray = stream.map(String::valueOf)
.filter(path -> path.endsWith("out"))
.toArray(size -> new String[size]);
}
if (stringArray.length > 0) {
log.info("read method - file found");
return stringArray[0];
} else {
log.info("read method - no file found");
return null;
}
}
}
//----
public class CustomItemProcessor implements ItemProcessor<String , String> {
@Autowired
@Qualifier(value="queue")
private Queue q;
@Autowired
private Environment env;
@Override
public String process(String s) throws Exception {
q.myMethod();
return s;
}
}
//----
public class CustomItemWriter implements ItemWriter<String> {
@Override
public void write(List<? extends String> s) throws Exception {
Path path1 = Paths.get(s, “notImportantDetail”);
java.nio.file.Files.deleteIfExists(path1);
}
}
//----
@Component
public class QueueScheduler {
private static final Logger log = LoggerFactory
.getLogger(QueueScheduler.class);
private Job job;
private JobLauncher jobLauncher;
@Autowired
public QueueScheduler(JobLauncher jobLauncher, @Qualifier("job") Job job){
this.job = job;
this.jobLauncher = jobLauncher;
}
@Scheduled(fixedRate=60000)
public void runJob(){
try{
jobLauncher.run(job, new JobParameters());
}catch(Exception ex){
log.info(ex.getMessage());
}
}
}
//----
:: Spring Boot :: (v1.3.1.RELEASE)
2016-01-08 14:51:44.783 INFO 7716 --- [main] com.example.DemoApplication:在 PID 7716 的 GH-VDIKCISV252 上启动 DemoApplication(C:\STS\wsRestTemplate\demo\target\classes 由 e049447 在C:\STS\wsRestTemplate\demo)
2016-01-08 14:51:44.788 INFO 7716 --- [main] com.example.DemoApplication:未设置活动配置文件,回退到默认配置文件:默认
2016-01-08 14:51:44.955 INFO 7716 --- [main] scaAnnotationConfigApplicationContext:刷新 org.springframework.context.annotation.AnnotationConfigApplicationContext@6e2c9341:启动日期 [Fri Jan 08 14:51:44 CST 2016];上下文层次的根
2016-01-08 14:51:50.882 WARN 7716 --- [main] oscaConfigurationClassEnhancer:@Bean 方法 ScopeConfiguration.stepScope 是非静态的,并返回一个可分配给 Spring 的 BeanFactoryPostProcessor 接口的对象。这将导致无法处理方法的声明@Configuration 类中的@Autowired、@Resource 和@PostConstruct 等注解。将“静态”修饰符添加到此方法以避免这些容器生命周期问题;有关完整的详细信息,请参阅@Bean javadoc。
2016-01-08 14:51:51.030 WARN 7716 --- [main] oscaConfigurationClassEnhancer:@Bean 方法 ScopeConfiguration.jobScope 是非静态的,并返回一个可分配给 Spring 的 BeanFactoryPostProcessor 接口的对象。这将导致无法处理方法的声明@Configuration 类中的@Autowired、@Resource 和@PostConstruct 等注解。将“静态”修饰符添加到此方法以避免这些容器生命周期问题;有关完整的详细信息,请参阅@Bean javadoc。
2016-01-08 14:51:51.386 INFO 7716 --- [main] osjdeEmbeddedDatabaseFactory:启动嵌入式数据库:url='jdbc:hsqldb:mem:testdb',username='sa'
2016-01-08 14:51:52.503 WARN 7716 --- [main] osbclAbstractListenerFactoryBean:org.springframework.batch.item.ItemReader 是一个接口。不会查询实现类以获取基于注释的侦听器配置。如果在 @Bean 方法上使用 @StepScope,请务必返回实现类,以便可以使用侦听器注释。
2016-01-08 14:51:53.572 INFO 7716 --- [main] osjdbc.datasource.init.ScriptUtils:从类路径资源 [org/springframework/batch/core/schema-hsqldb.sql] 执行 SQL 脚本
2016-01-08 14:51:53.667 INFO 7716 --- [main] osjdbc.datasource.init.ScriptUtils:从 94 中的类路径资源 [org/springframework/batch/core/schema-hsqldb.sql] 执行 SQL 脚本小姐。
2016-01-08 14:51:54.506 INFO 7716 --- [main] osjeaAnnotationMBeanExporter:在启动时为 JMX 公开注册 bean
2016-01-08 14:51:54.617 INFO 7716 --- [pool-2-thread-1] osbcrsJobRepositoryFactoryBean:未设置数据库类型,使用元数据指示:HSQL
2016-01-08 14:51:54.744 INFO 7716 --- [main] osbabJobLauncherCommandLineRunner:运行默认命令行:[]
2016-01-08 14:51:54.745 INFO 7716 --- [main] osbcrsJobRepositoryFactoryBean:未设置数据库类型,使用元数据指示:HSQL
2016-01-08 14:51:54.912 INFO 7716 --- [pool-2-thread-1] osbclsupport.SimpleJobLauncher:未设置任务执行器,默认为同步执行器。
2016-01-08 14:51:54.961 INFO 7716 --- [main] osbclsupport.SimpleJobLauncher:未设置任务执行器,默认为同步执行器。
2016-01-08 14:51:55.044 INFO 7716 --- [pool-2-thread-1] osbclsupport.SimpleJobLauncher:作业:[SimpleJob:[name=job1]] 使用以下参数启动:[{}]
2016-01-08 14:51:55.088 INFO 7716 --- [pool-2-thread-1] osbatch.core.job.SimpleStepHandler:执行步骤:[step1]
2016-01-08 14:51:55.095 INFO 7716 --- [main] osbclsupport.SimpleJobLauncher:作业:[SimpleJob:[name=job1]] 使用以下参数启动:[{run.id=1}]
2016-01-08 14:51:55.176 INFO 7716 --- [main] osbatch.core.job.SimpleStepHandler:执行步骤:[step1]
2016-01-08 14:51:55.245 INFO 7716 --- [main] com.example.CustomItemReader:读取方法 - 收集输出文件名
2016-01-08 14:51:55.314 INFO 7716 --- [pool-2-thread-1] com.example.CustomItemReader:读取方法 - 收集输出文件名
2016-01-08 14:51:55.440 INFO 7716 --- [pool-2-thread-1] com.example.CustomItemReader:读取方法 - 找到文件
2016-01-08 14:51:55.443 INFO 7716 --- [pool-2-thread-1] com.example.CustomItemProcessor:处理方法:
2016-01-08 14:51:55.461 INFO 7716 --- [main] com.example.CustomItemReader:读取方法 - 找到文件
2016-01-08 14:51:55.462 INFO 7716 --- [main] com.example.CustomItemProcessor:处理方法:
2016-01-08 14:51:57.088 错误 7716 --- [pool-2-thread-1] osbatch.core.step.AbstractStep:在作业 job1 中执行步骤 step1 时遇到错误
org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: 一个节点在与创建它的文档不同的文档中使用。
at com.sun.org.apache.xerces.internal.dom.ParentNode.internalInsertBefore(ParentNode.java:357) ~[na:1.8.0_45]
at com.sun.org.apache.xerces.internal.dom.ParentNode.insertBefore(ParentNode.java:288) ~[na:1.8.0_45]
at com.example.Queue.addField(Queue.java:1555) ~[classes/:na]
at com.example.Queue.addMessagesFromAuth(Queue.java:453) ~[classes/:na]
at com.example.CustomItemProcessor.process(CustomItemProcessor.java:45) ~[classes/:na]
at com.example.CustomItemProcessor.process(CustomItemProcessor.java:1) ~[classes/:na]
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:126) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:293) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:192) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) ~[spring-tx-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:271) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:392) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at com.sun.proxy.$Proxy44.run(Unknown Source) [na:na]
at com.example.QueueScheduler.runJob(QueueScheduler.java:33) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) [spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_45]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [na:1.8.0_45]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_45]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_45]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]
2016-01-08 14:51:57.104 INFO 7716 --- [pool-2-thread-1] osbclsupport.SimpleJobLauncher:作业:[SimpleJob:[name=job1]] 使用以下参数完成:[{}] 和以下状态:[失败]
2016-01-08 14:51:57.754 INFO 7716 --- [main] com.example.CustomItemWriter:写入方法:[C:\myApp\from\0000000571900000999674MHlog.txt.out]
2016-01-08 14:51:57.761 INFO 7716 --- [main] com.example.CustomItemReader:读取方法 - 收集输出文件名
2016-01-08 14:51:57.762 INFO 7716 --- [main] com.example.CustomItemReader:读取方法 - 未找到文件
2016-01-08 14:51:57.783 INFO 7716 --- [main] osbclsupport.SimpleJobLauncher:作业:[SimpleJob:[name=job1]] 使用以下参数完成:[{run.id=1}] 和以下状态:[已完成]
2016-01-08 14:51:57.786 INFO 7716 --- [main] com.example.DemoApplication:在 13.693 秒内启动了 DemoApplication(JVM 运行时间为 14.853)
2016-01-08 14:52:54.724 INFO 7716 --- [pool-2-thread-1] osbclsupport.SimpleJobLauncher:作业:[SimpleJob:[name=job1]] 使用以下参数启动:[{}]
2016-01-08 14:52:54.750 INFO 7716 --- [pool-2-thread-1] osbatch.core.job.SimpleStepHandler:执行步骤:[step1]
2016-01-08 14:52:54.755 INFO 7716 --- [pool-2-thread-1] com.example.CustomItemReader:读取方法 - 收集输出文件名
2016-01-08 14:52:54.756 INFO 7716 --- [pool-2-thread-1] com.example.CustomItemReader:读取方法 - 未找到文件
2016-01-08 14:52:54.775 INFO 7716 --- [pool-2-thread-1] osbclsupport.SimpleJobLauncher:作业:[SimpleJob:[name=job1]] 使用以下参数完成:[{}] 和以下状态:[已完成]