3

RSS/Atom フィード アグリゲーターを実装しようとしてspring-integrationいますが、主に Java DSL を使用してIntegrationFlow. このアグリゲーターの要件は、実行時にフィードを追加/削除できることです。つまり、フィードは設計時にはわかりません。

Feed.inboundAdapter()基本をテスト URL で使用し、トランスフォーマーを使用してフィードからリンクを抽出し、それを に渡してoutbound-file-adapterリンクをファイルに保存するのは簡単であることがわかりました。inbound-file-adapterただし、実行中のファイルから (数千の) フィード URL を読み取り、フィード URL を含むFileSplitter各結果を渡し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 は s しか参照していませんoutbound-adapter。インバウンドアダプターは実際には単なるサブクラスでAbstractMessageSourceあり、それらのいずれかを指定できる唯一IntegrationFlows.from(/*stuff here*/)の場所はメソッドの引数としてです。

ファイルから入力を取得し、行ごとに分割し、その出力を使用してインバウンド フィード アダプターを登録し、それらのフィードをポーリングし、表示されるフィードから新しいリンクを抽出してファイルに追加することが可能であると考えていました。 . そうではないかのように見えます。

これを機能させるためにできる賢いサブクラス化はありますか??

それが失敗すると...そしてこれが答えになると思います.春の統合動的Ftpチャネルリゾルバーの例と、それを適応させる方法に関するこの回答は、インバウンドケースに動的に登録する方法を見つけました...

それで、これは行く方法ですか?ヘルプ/ガイダンスをいただければ幸いです。何日も 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で機能し、 で定義された URL の1 つがある場合にのみ機能ます。そうしないと、「プロパティ {feed.url} を解決できません」というメッセージが表示されて失敗します。そこで何が起こっているのかと思うのは、すべてで定義された sが熱心に初期化されるため、メイン メソッドの for ループで手動で作成された Bean (feed.url プロパティが注入されているため正常に動作する) を除いて、熱心に初期化された迷子のシングルトンであり、application.properties で定義された feed.url がない場合、プロパティを解決できず、すべてがうまくいきません。Springについて私が知っていることから、Beanを初期化できるはずだとわかっていますapplication.propertiesfeed.url=[insert url here]@BeanDynamicFeedAdapter.java@LazyDynamicFeedAdapter.javaそのため、この望ましくない漂遊シングルトンの問題児に巻き込まれることはありません。問題は今です...マークしただけでfeedAdapter() @Lazyは、Beanは初期化されません。自分で初期化するにはどうすればよいですか?

更新 - 問題は解決しました

テストしていないので、問題は、ブートがコンポーネント スキャン中に DynamicFeedAdapter を見つけていることだと思います。簡単な解決策は、兄弟パッケージに移動することです。MonsterFeedApplication が com.acme.foo にある場合は、アダプタ構成クラスを com.acme.bar に配置します。そうすれば、ブートはそれをアプリケーションの「一部」と見なしません

これは確かに問題でした。Gary の提案を実装した後、すべてが完璧に機能します。

4

1 に答える 1