Apache Camel — запуск задачи при запуске для запуска только один раз

#java #spring #apache-camel

#java #весна #apache-camel

Вопрос:

Я работаю над проектом Java с использованием Camel amp; Spring. Мы хотели бы запустить метод инициализации для одноэлементного компонента после того, как Spring закончит свою работу, а Camel завершит построение всех маршрутов.

Мы не можем вызвать метод во время создания класса, поскольку он имеет динамические ссылки на другие классы, которые он выбирает из аннотации @Component spring, и мы не знаем, когда / если эти классы были загружены, чтобы фактически запустить метод init как часть конструктора.

Как я могу вызвать метод или методы для запуска только один раз сразу после завершения запуска Camel?

Спасибо!

Ответ №1:

другой простой вариант, который дает вам немного больше гибкости, — это использовать camel-timer с repeatCount= 1 и значением задержки, достаточным для инициализации всего. вы также можете добавить базовую обработку исключений для задержки / повторной попытки и т. Д…

 from("timer://runOnce?repeatCount=1amp;delay=5000").to("bean:runOnceBean");
  

Комментарии:

1. Не могли бы вы предоставить пример кода? Я изо всех сил пытаюсь заставить API from () использоваться в моем классе

2. Даже это должно сработать: from(«timer://RunOnce?repeatCount=1»).to(«bean: runOnceBean»); Я не думаю, что нам нужна задержка здесь, поскольку, если мы сохраняем это в маршруте, оно будет вызвано только тогда, когда контекст будет готов. Пожалуйста, дайте мне знать, если я где-то ошибаюсь.

3. правильно, задержка не требуется, если вы не ожидаете чего-то асинхронного с запуском контекста или не хотите, чтобы что-то было загружено и т. Д…

Ответ №2:

Если компонент должен быть вызван после того, как CamelContext запустил все маршруты и т.д., Тогда вы можете, как предлагает Бен, использовать маршрут с таймером.

Возможной лучшей альтернативой является использование API EventNotifier от Camel. А затем вызовите логику для запускаемого CamelContextStartedEvent . Некоторые подробности об API EventNotifier здесь: http://camel.apache.org/eventnotifier-to-log-details-about-all-sent-exchanges.html

Комментарии:

1. «СТРАНИЦА НЕ НАЙДЕНА» … другой пример EventNotifier: people.apache.org /~dkulp/верблюд/…

2. web.archive.org/web/20190126135728/https://camel.apache.org /… был все еще жив по состоянию на 2019 год

Ответ №3:

Добавьте логику в метод вашего компонента и прокомментируйте его с помощью @PostConstruct — spring вызовет этот метод, как только этот компонент будет полностью инициализирован и все его зависимости установлены.

 @Component
class SomeClass {

 @PostConstruct
 void init() {
 }

}
  

Если логику необходимо вызвать после полной инициализации всего контекста приложения spring, вы можете сделать это, реализовав интерфейс жизненного цикла.

Комментарии:

1. Спасибо. Является @PostConstruct глобальным в том смысле, что если я выполняю поиск для всех @Component s определенного типа, используя что-то вроде ApplicationContent.getBeansOfType(SomeType.class) , всегда ли он будет собирать экземпляры этих компонентов, даже если порядок загрузки пружины их меняется?

2. Есть ли способ обойти это без использования интерфейса жизненного цикла? Например, с помощью компонента вы можете <bean class="MyClass" init-method="start" destroy-method="stop"/> вызвать start() и stop() соответственно… Я надеялся, что может быть что-то подобное для контекста Camel… Какие-либо изменения у вас есть пример того, как использовать LifeCycle интерфейс?

3. @gkamal Это не сработает, поскольку инициализация CamelContext НЕ обязательно завершается после завершения контекста приложения spring. Следовательно, вызов post construct не является гарантией запуска контекста camel и доступности всех маршрутов.

Ответ №4:

Одним из решений было бы исправление пары файлов (см. PR https://github.com/apache/camel/pull/684 ): Конфигурация CamelContextConfiguration.java и RoutesCollector.java .

В CamelContextConfiguration добавьте метод:

 void afterApplicationStart(CamelContext camelContext);
  

И в onApplicationEvent of RoutesCollector добавьте что-то вроде:

         if (camelContextConfigurations != null) {
            for (CamelContextConfiguration camelContextConfiguration : camelContextConfigurations) {
                camelContextConfiguration.afterApplicationStart(camelContext);
            }
        }
  

Вы можете опустить if (camelContextConfigurations != null) , если используете последнюю версию на эту дату.

Затем создайте компонент Spring следующим образом, чтобы добавить свой код:

 @Bean
CamelContextConfiguration contextConfiguration() {
    return new CamelContextConfiguration() {

        @Override
        public void beforeApplicationStart(CamelContext camelContext) {
        }

        @Override
        public void afterApplicationStart(CamelContext camelContext) {
            // Put your code here
        }
    };
}
  

ОБНОВЛЕНИЕ: этот запрос на извлечение был объединен.

Ответ №5:

Вы можете использовать функциональность порядка запуска в Camel, задокументированную на http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html :-

 <route startupOrder="1" id="thisOneGoesFirst">    
  <from uri="seda:foo"/>
  <to uri="mock:result"/>
</route>
<route startupOrder="2" id="thisOneGoesSecond">
  <from uri="direct:start"/>
  <to uri="seda:foo"/>
</route> 
<route id="thisOneGoesLast">
  <from uri="direct:bar"/>
  <to uri="seda:bar"/>
</route>
  

где маршруты с атрибутом startupOrder будут выполняться по порядку и ПЕРЕД всеми маршрутами, у которых нет startupOrder . Таким образом, вы можете проложить свой маршрут с потребителем таймера в любой момент, когда захотите, до или после запуска ваших маршрутов.

Комментарии:

1. Предположим, вы определяете маршрут «thisOneGoesFirst» с помощью startupOrder =»1″ и с элементом «from», который вызывает таймер для запуска без задержки (например, «time: startup?repeatCount = 1 amp; delay = 0»). Будет ли логика внутри маршрута «thisOneGoesFirst» вызываться таймером перед логикой внутри маршрута «thisOneGoesSecond»? Мы знаем через «startupOrder», что таймер будет вызван первым, но моя интуиция подсказывает, что таймер просто вызовет другой поток для запуска логики внутри «thisOneGoesFirst», и будет ли этот поток вызван до логики внутри «thisOneGoesSecond», это вопрос случайности.

Ответ №6:

Вы можете попробовать внедрить контекст camel в свой одноэлементный компонент. Внедрение не произойдет, пока контекст не будет полностью инициализирован … включая построение всех маршрутов. Недостатком является то, что вам может не потребоваться контекст внутри вашего компонента. Я обдумываю идею связать зависимость одноэлементного компонента с camelContext инициализацией в файле конфигурации spring, но не уверен, что это действительно сработает.

Комментарии:

1. Это не сработает, поскольку инициализация CamelContext НЕ обязательно завершается после завершения контекста приложения spring.

Ответ №7:

Как уже указывалось в предыдущих ответах, это скорее проблема Spring, чем Camel. Весной вы можете просто реализовать InitializingBean и реализовать menthod afterPropertiesSet . Это вызывается, когда подключение выполнено.