Spring Batch和Spring Integration的集成问题 - “没有为端点定义轮询器”异常

时间:2014-07-01 18:17:01

标签: java spring junit spring-batch spring-integration

我浏览了Spring Integration guideexamples here,并获得了Spring Integration SFTP程序的工作示例。我已经有一个工作的Spring Batch程序,它读取一堆文件并转储到数据库中。

我现在尝试通过Spring docs整合Spring Batch和Spring Integration程序,并创建了以下配置。

<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
        <property name="host" value="${host}"/>
        <property name="port" value="${port}"/>
        <property name="user" value="${user}"/>
        <property name="password" value="${password}"/>
    </bean>

    <int:channel id="inboundFileChannel"><int:queue/></int:channel>
    <int:channel id="outboundJobRequestChannel"/>
    <int:channel id="jobLaunchReplyChannel"/>

    <int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
            channel="inboundFileChannel"
            session-factory="sftpSessionFactory"
            local-directory="file:/chofac/data/mex/registry/outbox"
            remote-directory="/chofac/SFTP/MEX/outbox"
            auto-create-local-directory="true"
            delete-remote-files="false"
            filename-pattern="*.txt">
        <int:poller max-messages-per-poll="-1" fixed-rate="1000" />
    </int-sftp:inbound-channel-adapter>

    <int:transformer input-channel="inboundFileChannel"
        output-channel="outboundJobRequestChannel">
        <bean class="com.chofac.mint.integration.FileMessageToJobRequest">
            <property name="job" ref="responseFileReaderJob"/>
            <!-- <property name="fileParameterName" value="input.file.name"/> -->
        </bean>
    </int:transformer>

    <batch-int:job-launching-gateway request-channel="outboundJobRequestChannel"
        reply-channel="jobLaunchReplyChannel" job-launcher="jobLauncher">
        <int:poller fixed-rate="1000"/>
    </batch-int:job-launching-gateway>

    <int:logging-channel-adapter channel="jobLaunchReplyChannel"/>

    <batch:job id="responseFileReaderJob">
        <batch:step id="dailyReaderJob">
            <batch:tasklet>
                <batch:chunk reader="dailyRRReader" writer="dailyRRDBWriter" processor="itemProcessor" commit-interval="10"/>
            </batch:tasklet>
        </batch:step>
    </batch:job>

我正在使用下面的测试用例运行此程序:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:META-INF/spring/applicationContext.xml","classpath:META-INF/spring/inbound-ResponseReaderJobIntegration.xml"})
public class AAASftpInboundMsgJobTriggerTest {

    @Resource(name="inboundFileChannel")
    PollableChannel localFileChannel;

    @Test
    public void runDemo(){
        System.out.println("Received first file message: " + localFileChannel.receive());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我收到此错误:

  

2014-07-01 10:51:48,987 [主要] INFO   org.hibernate.impl.SessionFactoryImpl - 结束2014-07-01   10:51:48,988 [主要] DEBUG   org.springframework.beans.factory.support.DisposableBeanAdapter -   调用destroy方法&#39;关闭&#39;在名为&#39; dataSource&#39;的bean上   2014-07-01 10:51:48,988 [主要]错误   org.springframework.test.context.TestContextManager - 捕获异常   同时允许TestExecutionListener   [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2faadcc6]   准备测试实例   [com.chofac.mint.integration.AAASftpInboundMsgJobTriggerTest@5d3ad33d]   java.lang.IllegalStateException:无法加载ApplicationContext   org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)     在   org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)     在   org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)     在   org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)     在   org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:326)     在   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)at   org.springframework.test.context.junit4.SpringJUnit4ClassRunner $ 1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)     在   org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)     在   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)     在   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)     在   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)     在org.junit.runners.ParentRunner $ 3.run(ParentRunner.java:231)at   org.junit.runners.ParentRunner $ 1.schedule(ParentRunner.java:60)at   org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)at at   org.junit.runners.ParentRunner.access $ 000(ParentRunner.java:50)at   org.junit.runners.ParentRunner $ 2.evaluate(ParentRunner.java:222)at at   org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)     在   org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)     在org.junit.runners.ParentRunner.run(ParentRunner.java:300)at   org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)     在   org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)     在   org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)     在   org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)     在   org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)     在   org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)     在   org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)   引起:org.springframework.beans.factory.BeanCreationException:   使用名称创建bean时出错   &#39; org.springframework.integration.config.ConsumerEndpointFactoryBean#0&#39 ;:   调用init方法失败;嵌套异常是   java.lang.IllegalArgumentException:没有定义轮询器   端点   &#39; org.springframework.integration.config.ConsumerEndpointFactoryBean#0&#39 ;,   并且在上下文中没有可用的默认轮询器。在   org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553)     在   org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)     在   org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)     在   org.springframework.beans.factory.support.AbstractBeanFactory $ 1.getObject(AbstractBeanFactory.java:304)     在   org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)     在   org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)     在   org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)     在   org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:681)     在   org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)     在   org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)     在   org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)     在   org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)     在   org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)     在   org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)     在   org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)     在   org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)     ... 25更多引起:java.lang.IllegalArgumentException:没有轮询器   已为端点定义   &#39; org.springframework.integration.config.ConsumerEndpointFactoryBean#0&#39 ;,   并且在上下文中没有可用的默认轮询器。在   org.springframework.util.Assert.notNull(Assert.java:112)at   org.springframework.integration.config.ConsumerEndpointFactoryBean.initializeEndpoint(ConsumerEndpointFactoryBean.java:238)     在   org.springframework.integration.config.ConsumerEndpointFactoryBean.afterPropertiesSet(ConsumerEndpointFactoryBean.java:187)     在   org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)     在   org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)     ... 40更多2014-07-01 10:51:48,992 [主要] DEBUG   org.springframework.test.context.support.DirtiesContextTestExecutionListener    - 测试类之后:context [DefaultTestContext @ ac44b88 testClass = SftpInboundMsgJobTriggerTest,testInstance = [null],testMethod =   [null],testException = [null],mergedContextConfiguration =   [MergedContextConfiguration @ 4102799c testClass =   SftpInboundMsgJobTriggerTest,locations =   &#39; {类路径:META-INF /弹簧/ applicationContext.xml中,   类路径:META-INF /弹簧/入站ResponseReaderJobIntegration.xml.xml}&#39 ;,   classes =&#39; {}&#39;,contextInitializerClasses =&#39; []&#39;,activeProfiles =   &#39; {}&#39;,contextLoader =   &#39; org.springframework.test.context.support.DelegatingSmartContextLoader&#39 ;,   parent = [null]]],dirtiesContext [false]。

我尝试了Google的大部分top answers on this issue以及StachOverflow的建议,因为我输入了这个问题,所有这些都导致了其他不同的错误,而且我似乎正在偏离主要问题。

最常见的建议是添加全局轮询器,但这会导致以下错误:

<int:poller default="true" fixed-delay="50"/>
  

引起:java.lang.IllegalArgumentException:轮询器不应该是   为端点指定   &#39; org.springframework.integration.config.ConsumerEndpointFactoryBean#1&#39 ;,   因为&#39; outboundJobRequestChannel&#39;是一个SubscribableChannel(不是   可轮询)。

(我是所有这些中的新手,Spring,Spring Batch和Spring Integration)任何帮助都将不胜感激。提前谢谢。

更新1

我删除了#2中的轮询器,如下所示

<batch-int:job-launching-gateway request-channel="outboundJobRequestChannel"
    reply-channel="jobLaunchReplyChannel" job-launcher="jobLauncher">
</batch-int:job-launching-gateway>

并删除了全局轮询器

<int:poller default="true" fixed-delay="50"/> 

我得到以下异常:

线程中的异常&#34; main&#34; org.springframework.beans.factory.BeanCreationException:使用名称&#39; org.springframework.integration.config.ConsumerEndpointFactoryBean创建bean时出错#0&#39;:init方法的调用失败;嵌套异常是java.lang.IllegalArgumentException:没有为端点&org.springframework.integration.config.ConsumerEndpointFactoryBean#0&#39;定义轮询器,并且在上下文中没有可用的默认轮询器。     在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553)     在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)     在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)     at org.springframework.beans.factory.support.AbstractBeanFactory $ 1.getObject(AbstractBeanFactory.java:304)     在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)     在org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)     在org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)     at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:681)     在org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)     在org.springframework.context.support.ClassPathXmlApplicationContext。(ClassPathXmlApplicationContext.java:139)     在org.springframework.context.support.ClassPathXmlApplicationContext。(ClassPathXmlApplicationContext.java:93)     在com.chofac.mint.integration.DownloadFileRunBatch.main(DownloadFileRunBatch.java:15) 引起:java.lang.IllegalArgumentException:没有为端点&org.springframework.integration.config.ConsumerEndpointFactoryBean#0&#39;定义轮询器,并且在上下文中没有可用的默认轮询器。     在org.springframework.util.Assert.notNull(Assert.java:112)     在org.springframework.integration.config.ConsumerEndpointFactoryBean.initializeEndpoint(ConsumerEndpointFactoryBean.java:238)     在org.springframework.integration.config.ConsumerEndpointFactoryBean.afterPropertiesSet(ConsumerEndpointFactoryBean.java:187)     在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)     在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)     ......还有12个

然而...... 如果我离开全局轮询器,则会发生SFTP传输并触发作业

<int:poller default="true" fixed-delay="50"/>

更新2

如果我删除

,我会收到以下异常
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'inboundFileChannel' must be of type [org.springframework.messaging.PollableChannel], but was actually of type [org.springframework.integration.channel.DirectChannel]  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:376)    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)  at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:979)  at com.chofac.mint.batchintegration.SftpInboundMsgJobTriggerMain.main(SftpInboundMsgJobTriggerMain.java:16)

这是我的配置:

<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
        channel="inboundFileChannel"
        session-factory="sftpSessionFactory"
        local-directory="file:${inbound.local.directory}"
        remote-directory="${inbound.remote.directory}"
        auto-create-local-directory="true"
        delete-remote-files="false"
        filename-pattern="*.*">
    <int:poller max-messages-per-poll="-1" fixed-rate="45000" />
    <!-- <int:poller max-messages-per-poll="-1" cron="${MEX.CRON.PATTERN}"/> -->
    <!-- 0 15 10 ? * MON-FRI -->
</int-sftp:inbound-channel-adapter>

<int:channel id="inboundFileChannel"></int:channel>
<int:channel id="outboundJobRequestChannel"/>
<int:channel id="jobLaunchReplyChannel"/>

<!-- <int:poller default="true" cron="${MEX.CRON.PATTERN}"/> -->

<int:transformer input-channel="inboundFileChannel"
    output-channel="outboundJobRequestChannel">
    <bean class="com.chofac.mint.integration.FileMessageToJobRequest">
        <property name="job" ref="responseFileReaderJob"/>
    </bean>
</int:transformer>

<batch-int:job-launching-gateway request-channel="outboundJobRequestChannel"
    reply-channel="jobLaunchReplyChannel" job-launcher="jobLauncher">
</batch-int:job-launching-gateway>

<int:logging-channel-adapter channel="jobLaunchReplyChannel"/>

我从主程序触发此操作如下:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml","classpath:META-INF/spring/batchintegration/inboundSFTPJob.xml");
PollableChannel pollableFileChannel = context.getBean("inboundFileChannel", PollableChannel.class);
System.out.println("Received first file message: " + pollableFileChannel.receive());

更新3

网络上的示例ConfigurationJUnitSpring example

更新4

<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
        channel="inboundFileChannel"
        session-factory="sftpSessionFactory"
        local-directory="file:${inbound.local.directory}"
        remote-directory="${inbound.remote.directory}"
        auto-create-local-directory="true"
        delete-remote-files="false" 
        filename-pattern ="*.*"
        local-filter="acceptAllFileListFilter">
    <int:poller max-messages-per-poll="-1" fixed-rate="45000" />

<bean id="acceptAllFileListFilter" class="org.springframework.integration.file.filters.AcceptAllFileListFilter"/>

程序运行到具有异常的连续循环。虽然我已经为轮询器Full configuration and logs here指定了45秒,但我觉得轮询频繁发生(每秒一次)。

1 个答案:

答案 0 :(得分:4)

outboundJobRequestChannelSubscribableChannel,因此您无法在batch-int:job-launching-gateway上拥有<poller/>inboundFileChannelQueueChannel,因此其消费者需要一个轮询器(变换器)。

注意bean名称中的#0#1int-sftp:inbound-channel-adapter#0)正确地有一个轮询器;只需删除另一个。

对于这种情况,你真的不需要一个全局(默认)轮询器(但它正用于你的变换器)。

编辑:

(回复你问题的评论)。

这是默认行为。默认情况下,local-filterAcceptOnceFileListFilter。如果您在下次投票之前删除该文件,则可以将其更改为AcceptAllFileListFilter

如果要将文件保留在磁盘上,但检测到文件已更改,请使用FileSystemPersistentAcceptOnceFileListFilter;由于您没有删除远程文件,因此您还应将filter设置为SftpPersistentAcceptOnceFileListFilter(实际上是CompositeFileListFilter包装此过滤器和其中一个模式过滤器。)

否则,您将继续提取相同的文件,如果没有preseve-timestamp,本地过滤器会认为每次都是新文件。

持久性过滤器使用元数据存储并使用文件名和lastModified时间戳来确定过滤器是否应接受(传递)文件。

EDIT2:

该示例只是将文件转储到队列通道并在main中接收;你订购了变压器。

这段代码......

PollableChannel pollableFileChannel = context.getBean("inboundFileChannel", PollableChannel.class);
System.out.println("Received first file message: " + pollableFileChannel.receive());

导致你当前的悲痛 - 只需从主要部分删除这些行。