Я пытаюсь реализовать агрегатор подачи RSS/Atom в spring-integration
, и я в основном использую Java DSL для написания своего IntegrationFlow
. Требование этого агрегатора заключается в том, что каналы могут быть добавлены/удалены во время выполнения. То есть, каналы не известны во время разработки.Как динамически регистрировать входящий входящий адаптер в Spring Integration?
Мне было просто использовать базовый Feed.inboundAdapter()
с тестовым URL-адресом и извлечь ссылки из фида с помощью трансформатора, а затем передать его на outbound-file-adapter
, чтобы сохранить ссылки на файл. Тем не менее, я очень сильно застрял, пытаясь прочитать (тысячи) корневых URL-адресов из файла inbound-file-adapter
, запустив файл через FileSplitter
, а затем передайте каждый полученный Message<String>
, содержащий URL-адрес канала, чтобы зарегистрировать новый Feed.inboundAdapter()
. Не возможно ли это с помощью DSL Java?
В идеале я хотел бы, если бы я мог сделать следующее:
@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 коды (и обучение тонны материала по пути) Я просто не могу» видеть, что это можно сделать так. Итак ... А) не так ли? B) должно ли это быть? C) Предложения?
Почти похоже на то, что я должен принимать вывод .handle(new FileSplitter())
и передавать его в .handleWithAdapter(Feed.inboundAdapter(/*stuff here*/))
, но DSL ссылается только на outbound-adapter
. Входящие адаптеры на самом деле просто подкласс AbstractMessageSource
, и кажется только место, которое вы можете указать, один из них является аргументом метода IntegrationFlows.from(/*stuff here*/)
.
Я бы подумал, что можно взять входные данные из файла, разбить его по строкам, использовать этот вывод для регистрации входящих фидов, опроса этих каналов, извлечения новых ссылок из фидов по мере их появления и добавления их в файл. Кажется, что это не так.
Есть ли какие-то умные подклассы, которые я могу сделать, чтобы сделать эту работу?
В противном случае ... и я подозреваю, что это будет ответ, я нашел интеграцию пружинного Dynamic Ftp Channel Resolver Example и this ответ о том, как адаптировать его динамически регистрировать материал для въездного случае ...
Так это путь? Любая помощь/руководство оценены. После того, как вы пролили код DSL и прочитали документацию в течение нескольких дней, я думаю, что я буду участвовать в реализации динамического примера ftp и адаптирую его для работы с FeedEntryMessageSource ... в этом случае мой вопрос ... что работает динамический пример ftp с конфигурацией XML, но возможно ли это сделать либо с помощью Java config, либо с DSL Java?
Update
Я реализовал решение следующим образом:
@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 У меня есть o ne URL-адресов, определенных в application.properties
как feed.url=[insert url here]
.В противном случае он не сказал мне «неспособный разрешить свойство {feed.url}». Я подозреваю, что здесь происходит то, что @Bean
s, определенные в DynamicFeedAdapter.java
, все получают синглтоны, нетерпеливо инициализированные, поэтому в стороне от того, что бобы создаются вручную в нашем цикле for в основном методе (который отлично работает, потому что у них есть свойство feed.url), мы иметь бездомный синглтон, который был нетерпеливо инициализирован, и если нет feed.url, определенному в application.properties, тогда он не может разрешить свойство, и все идет ударом. Теперь из того, что я знаю о Spring, я знаю, что должно быть возможно, чтобы @Lazy
инициализировал компоненты в DynamicFeedAdapter.java
, поэтому мы не завершаем эту проблему нежелательной случайной проблемой singleton. Проблема в том, что ... если я просто отметю feedAdapter()
@Lazy
, тогда бобы никогда не будут инициализированы. Как инициализировать их самостоятельно?
Update - проблема решена
Без испытав его, я думаю, что проблема в том, что загрузочный найти DynamicFeedAdapter во время его компоненты сканирования. Простым решением является , чтобы переместить его в пакет для родственников. Если MonsterFeedApplication находится в com.acme.foo, тогда установите класс конфигурации адаптера в com.acme.bar. То, что способ, ботинок не будет считаться «частью» приложения
Это действительно была проблема. После реализации предложения Гэри все работает отлично.
Спасибо за помощь. Гэри. Я реализовал решение в соответствии с первой ссылкой, на которую вы ссылались, и я получаю проблему с введенными свойствами. Я делаю все так же, как в примере, поэтому создаю дочерний контекст, устанавливая родителя, настраивая свойства, устанавливая их в среде, а затем устанавливая среду для дочернего элемента и обновляя. У меня также есть PropertySourcesPlaceholderConfigurer @Bean в моем «DynamicFeedAdapter.class», который является классом, который я вызываю register(). Он продолжает говорить: «Не удалось разрешить placeholder» feed.url в строчном значении «$ {feed.url}». –
Редактируйте свой вопрос с помощью текущего кода. Вероятно, сегодня я не смогу посмотреть на него, но завтра я посмотрю. –
Прохладный, да, я сделаю позже, когда вернусь с работы. Цените помощь. –