如何在Spring Integration中动态注册Feed Inbound Adapter?

时间:2016-03-28 08:37:39

标签: rss spring-integration dsl atom-feed enterprise-integration

我正在尝试在spring-integration中实现RSS / Atom提要聚合器,我主要使用Java DSL来编写IntegrationFlow。此聚合器的要求是可以在运行时添加/删除提要。也就是说,在设计时,饲料未知

我发现将基本Feed.inboundAdapter()与测试网址一起使用很简单,然后使用变换器从Feed中提取链接,然后将其传递给outbound-file-adapter以保存指向文件的链接。但是,当我尝试通过inbound-file-adapterFileSplitter运行文件中读取(数千)Feed网址时,我已经陷入困境,然后将包含Feed网址的每个结果Message<String>传递给然后注册一个新的Feed.inboundAdapter()。这对Java DSL来说不可能吗?

理想情况下,如果我能做到以下情况,我会喜欢它:

@Bean
public IntegrationFlow getFeedsFromFile() throws MalformedURLException {
    return IntegrationFlows.from(inboundFileChannel(), e -> e.poller(Pollers.fixedDelay(10000)))
            .handle(new FileSplitter())
            //register new Feed.inboundAdapter(payload.toString()) foreach Message<String> containing feed url coming from FileSplitter
            .transform(extractLinkFromFeedEntry())
            .handle(appendLinkToFile())
            .get();
} 

虽然经过多次阅读弹簧集成java DSL代码(并沿途学习了大量的东西)后,我却看不出这样做是可行的。那么...... A)是吗? B)应该是吗? C)建议?

几乎感觉我应该能够获取.handle(new FileSplitter())的输出并将其传递给.handleWithAdapter(Feed.inboundAdapter(/*stuff here*/)),但DSL仅在那里引用outbound-adapter。入站适配器实际上只是AbstractMessageSource的子类,似乎唯一位置可以指定其中一个作为IntegrationFlows.from(/*stuff here*/)方法的参数。

我原本以为可以从文件中获取输入,逐行拆分,使用该输出注册入站Feed适配器,轮询这些Feed,从Feed中提取新链接并附加它们到一个文件。它看起来好像不是。

是否有一些聪明的子类我可以做这个工作?

失败了...我怀疑这将是答案,我发现Spring集成Dynamic Ftp Channel Resolver Examplethis回答如何适应它动态注册入站案例的东西..

这是要走的路吗?任何帮助/指导表示赞赏。在倾听了DSL代码和阅读文档几天之后,我想我将实现动态ftp示例并使其适应FeedEntryMessageSource ......在这种情况下我的问题是......动态ftp示例有效使用XML配置,但是可以使用Java配置或Java DSL吗?

更新

我已经按如下方式实施了解决方案:

@SpringBootApplication 
class MonsterFeedApplication {

public static void main(String[] args) throws IOException {
    ConfigurableApplicationContext parent = SpringApplication.run(MonsterFeedApplication.class, args);

    parent.setId("parent");
    String[] feedUrls = {
            "https://1nichi.wordpress.com/feed/",
            "http://jcmuofficialblog.com/feed/"};

    List<ConfigurableApplicationContext> children = new ArrayList<>();
    int n = 0;
    for(String feedUrl : feedUrls) {
        AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
        child.setId("child" + ++n);
        children.add(child);
        child.setParent(parent);
        child.register(DynamicFeedAdapter.class);
        StandardEnvironment env = new StandardEnvironment();
        Properties props = new Properties();
        props.setProperty("feed.url", feedUrl);
        PropertiesPropertySource pps = new PropertiesPropertySource("feed", props);
        env.getPropertySources().addLast(pps);
        child.setEnvironment(env);
        child.refresh();
    }

    System.out.println("Press any key to exit...");
    System.in.read();
    for (ConfigurableApplicationContext child : children) {
        child.close();
    }
    parent.close();
}

@Bean
public IntegrationFlow aggregateFeeds() {       
    return IntegrationFlows.from("feedChannel")
            .transform(extractLinkFromFeed())
            .handle(System.out::println)
            .get();
}

@Bean
public MessageChannel feedChannel() {
    return new DirectChannel();
}

@Bean
public AbstractPayloadTransformer<SyndEntry, String> extractLinkFromFeed() {
    return new AbstractPayloadTransformer<SyndEntry, String>() {
        @Override
        protected String transformPayload(SyndEntry payload) throws Exception {
            return payload.getLink();
        }
    };

}

}

DynamicFeedAdapter.java

@Configuration
@EnableIntegration
public class DynamicFeedAdapter {

    @Value("${feed.url}")
    public String feedUrl;

    @Bean
    public static PropertySourcesPlaceholderConfigurer pspc() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public IntegrationFlow feedAdapter() throws MalformedURLException {

        URL url = new URL(feedUrl);

        return IntegrationFlows
                .from(s -> s.feed(url, "feedTest"), 
                        e -> e.poller(p -> p.fixedDelay(10000)))
                .channel("feedChannel")
                .get();
    }

}

这适用于 IF ,只有 IF 我在application.properties中定义的一个作为feed.url=[insert url here] 。否则它无法告诉我'无法解析财产{feed.url}'。我怀疑正在发生的事情是@Bean中定义的DynamicFeedAdapter.java都得到了热切初始化的单例,所以除了在main方法中我们的for循环中手动创建的bean之外(因为它们工作得很好)注入了feed.url属性)我们有一个已经急切初始化的流浪单例,如果在application.properties中没有定义feed.url那么它就无法解析属性而且一切都响了。现在根据我对Spring的了解,我知道应该可以@Lazy初始化DynamicFeedAdapter.java中的bean,这样我们就不会遇到这个不需要的流浪单身问题 - 孩子了。问题是现在......如果我只标记feedAdapter() @Lazy那么bean永远不会被初始化。我如何自己初始化它们?

更新 - 问题已解决

  

没有经过测试,我认为问题在于启动正在寻找   组件扫描期间的DynamicFeedAdapter。一个简单的解决方案   把它移到一个兄弟包。如果MonsterFeedApplication在   com.acme.foo,然后将适配器配置类放在com.acme.bar中。那   方式,启动不会将其视为应用程序的“部分”

这确实是个问题。在实施加里的建议后,一切都很完美。

1 个答案:

答案 0 :(得分:1)

有关入站邮件适配器的类似问题,请参阅the answer to this questionits follow up

实质上,每个Feed适配器都是在参数化的子上下文中创建的。

在这种情况下,子上下文是使用main()方法创建的,但没有理由在.handle()调用的服务中无法完成。