Camel в вопросах и ответах

    6-7 апреля в Москве в шестой раз пройдет JPoint – международная Java-конференция для опытных разработчиков. В этот раз в ней примет участие Клаус Ибсен (Claus Ibsen) – старший инженер Red Hat, гуру Apache. Ниже приводим перевод начала его книги «Camel in Action» – эксклюзивно для читателей нашего блога.




    Знакомство c Camel


    Создавать комплексные системы с чистого листа – дело затратное и почти всегда обреченное на провал. Гораздо эффективнее и безопаснее собирать их как паззл из уже имеющихся и проверенных компонентов. В повседневной жизни мы часто полагаемся на такие интегрированные системы: от телефонной связи и финансовых транзакций до медицинских услуг и планирования поездок и развлечений.

    Паззл можно собрать, только если у вас есть все его фрагменты, и они легко и надежно соединяются друг с другом. То же верно и для проектов по интеграции систем. Но если кусочки настоящего паззла созданы для того, чтобы сложиться в единую картинку, то с ИТ-системами дело обстоит, как правило, наоборот. Как разработчика, вас, как правило, не очень волнует внутреннее устройство и работа интегрируемых систем, вам гораздо интереснее, как взаимодействовать с ним извне. Хороший фреймворк (платформа) интеграции предполагает две вещи: во-первых, простой и удобный аппарат абстракций для описания сложных интегрируемых систем, а во-вторых, связующий механизм, позволяющий объединить их в единое целое.

    Примером такой платформы является Apache Camel. Эта книга поможет понять, что такое Camel, как им пользоваться и почему мы считаем его одной из лучших платформ интеграции. Первая глава начинается со знакомства с Camel и описания его главных возможностей. Затем мы рассмотрим дистрибутив Camel и объясним, как выполнять приведенные в книге примеры. В конце главы мы представим основные концепции Camel, необходимые для понимания его архитектуры.

    Готовы? Тогда встречайте Camel.

    Представляем Camel


    Camel – это платформа интеграции приложений, созданная для того, чтобы работать продуктивно и с удовольствием. История Camel началась в 2007 году, и сегодня это зрелый проект с открытым кодом, который распространяется по лицензии Apache 2 и вокруг которого сложилось мощное сообщество разработки.

    Основная задача Camel – упростить интеграцию. Мы надеемся, что, прочитав эту книгу, вы по достоинству оцените возможности Camel и включите его в свой арсенал обязательных инструментов.

    Camel входит в семейство проектов Apache, получив свое имя благодаря краткости и простоте запоминания. Есть версия, что проект обязан именем марке сигарет, которые курил один из его основателей. Другие версии можно найти в списке вопросов и ответов на сайте проекта.

    Что такое Camel


    Ядром платформы Camel является механизм маршрутизации, точнее говоря, компоновщик механизмов маршрутизации. С помощью компоновщика вы можете задавать собственные правила маршрутизации, решать, из каких источников принимать сообщения, и определять, как обрабатывать и отправлять эти сообщения дальше. Интеграционный язык Camel позволяет задавать сложные правила маршрутизации, близкие к бизнес-процессам. Как показано на Рис. 1.1, Camel – это связующее звено между не связанными друг с другом системами.

    Один из основополагающих принципов Camel состоит в том, что он не делает никаких предположений в отношении обрабатываемых данных. Это важный момент, поскольку он позволяет вам, как разработчику, интегрировать системы самых разных типов без преобразования данных в канонический формат.

    Рис. 1.1 Camel – это связующее звено между не связанными друг с другом системами.



    Благодаря абстракциям высокого уровня Camel позволяет применять одни и те же API для взаимодействия с разными системами, использующими разные протоколы и типы данных. API для конкретных протоколов и типов данных реализуются в виде соответствующих компонентов. По умолчанию, Camel поддерживает более 280 протоколов и типов данных, и благодаря расширяемой модульной архитектуре позволяет самостоятельно реализовать поддержку необходимых протоколов, как проприетарных, так и открытых. Это избавляет от ненужного преобразования данных и делает Camel не только более быстрым, но и менее прожорливым. Как следствие, Camel отлично подходит для встраивания в проекты, где требуются развитые возможности обработки. В частности, Camel уже используются для задач интеграции в таких проектах с открытым кодом, как Apache ServiceMix, Karaf и ActiveMQ.

    Теперь о том, чем Camel не является. Это не сервисная шина ESB, хотя некоторые и называют его облегченной версией ESB, поскольку он поддерживает маршрутизацию, трансформацию, оркестрацию, мониторинг и другие функции. У Camel нет контейнера и шины надежного обмена сообщениями, хотя он и может использоваться в таком качестве, например, в уже упомянутом Apache ServiceMix. Поэтому мы предпочитаем называть Camel платформой интеграции, а не решением ESB.

    Если при упоминании ESB вам сразу же приходят на ум сложные монструозные системы, то спешим успокоить: Camel с тем же успехом можно применять и в совсем маленьких вещах, наподобие микросервисов или шлюзов IoT (интернета вещей).

    Чтобы понять, чем же является Camel, рассмотрим его основные функции.

    Зачем использовать Camel


    Camel – это практическое воплощение новых идей в области интеграции. Далее мы подробно рассмотрим возможности Camel, а пока что перечислим их:

    • Механизм посредничества и маршрутизации
    • Расширяемая библиотека компонентов
    • Шаблоны интеграции корпоративных приложений (EIP)
    • Предметно-ориентированный язык (DSL)
    • Маршрутизатор, не зависящий от формата полезной нагрузки
    • Архитектура на основе подключаемых модулей
    • Модель Plain Old Java Object (POJO)
    • Простота конфигурирования
    • Автоматические конверторы типов
    • Легковесное ядро, пригодное для встраивания в микросервисы
    • Поддержка облачных технологий
    • Пакет средств тестирования
    • Активное сообщество разработки

    Теперь разберем этот список по пунктам.

    Механизм посредничества и маршрутизации


    Механизм посредничества и маршрутизации – это ядро Camel. Механизм маршрутизации выборочно перемещает сообщения согласно конфигурации маршрута. В Camel маршруты настраиваются с помощью шаблонов интеграции и предметного языка (о них чуть позже).

    Расширяемая библиотека компонентов


    Camel имеет обширную библиотеку из более чем 280 компонентов, позволяющих ему подключаться через разнообразные транспорты, использовать различные API и понимать разные форматы данных. На Рис. 1.2. вы наверняка найдете технологии, которые уже использовали или хотели бы использовать. Разумеется, в рамках этой книги невозможно рассмотреть все компоненты Camel, но два десятка основных из них мы все же обсудим.

    Рис. 1.2 Возможность подключиться к чему угодно: Camel поддерживает более 280 транспортов, API и форматов данных.



    Шаблоны интеграции корпоративных приложений (EIP)


    При всем разнообразии проблем интеграции многие из них, как заметили Грегор Хоп (Gregor Hohpe) и Бобби Вульф (Bobby Woolf), имеют сходные решения. Эти двое выпустили книгу «Enterprise Integration Patterns» (переведена на русский – «Шаблоны интеграции корпоративных приложений»), которую должен прочесть каждый, кто считает себя специалистом в области интеграции приложений. Кроме того, эта книга поможет вам гораздо быстрее и легче понять концепции Camel.

    Шаблоны интеграции (EIP) полезны не только тем, что предлагают проверенные решения для тех или иных проблем интеграции, но также и потому, что упрощают описание и представление самих этих проблем, предоставляя соответствующую семантику. Camel в значительной степени основан на шаблонах EIP. Однако, хотя шаблоны EIP и описывают проблемы и решения, а также предоставляют общий словарь, этот словарь не формализован. Camel устраняет этот пробел, предоставляя язык для описания интеграционных решений. Между шаблонами, описанными в книге «Enterprise Integration Patterns», и предметным языком CamelDSL имеется почти взаимно однозначное соотношение.

    Предметно-ориентированный язык (DSL)


    На момент появления Camel его главным вкладом в дело интеграции стал предметно-ориентированный язык (DSL). Со временем этому примеру последовали и другие интеграционные фреймворки, и теперь DSL есть в Java, XML и других языках программирования. DSL позволяет разработчику сфокусироваться на задачах интеграции, не отвлекаясь на особенности используемого инструмента (языка программирования). Рассмотрим парочку примеров DSL, которые используют разные форматы, но полностью эквивалентны по функциональности:

    Java DSL

    1	from("file:data/inbox").to("jms:queue:order");
    

    XML DSL

    1	<route>
    2	  <from uri="file:data/inbox"/>
    3	  <to uri="jms:queue:order"/>
    4	</route>
    

    Это реальный код, и по нему видно, насколько просто отправить файл из нужной папки в очередь JMS (Java Message Service). Поскольку в основе DSL лежит популярный язык программирования, разработчику по-прежнему доступны привычные и удобные инструменты IDE-среды, наподобие автозавершения при вводе или обнаружения ошибок компилятора, как показано на Рис. 1.3.

    Рис. 1.3 DSL-языки Camel построены на основе популярных языков программирования вроде Java, поэтому вам доступны удобные и привычные функции IDE-среды.


    На рисунке показано, как функция автозавершения при вводе в Eclipse IDE предлагает список возможных DSL-выражений.

    Маршрутизатор, не зависящий от формата полезной нагрузки


    Camel умеет маршрутизировать полезные данные в любом виде, не ограничиваясь каким-то одним нормализованным форматом, например, полезной нагрузкой XML. Поэтому для маршрутизации нагрузки ее не надо преобразовывать в канонический формат.

    Архитектура на основе подключаемых модулей


    Модульная архитектура Camel позволяет загружать в него любые компоненты, как входящие в комплект поставки, так и те, что созданы сторонними разработчиками, в том числе и вами. Кроме того, в Camel можно настроить почти все, многие его функции являются подключаемыми и настраиваемыми: генерация ID, управление потоками, последовательность завершения, кэширование стримов и многое другое.

    Модель POJO


    Bean-компоненты Java (или Plain Old Java Objects, POJO) в Camel являются гражданами первого сорта, поэтому в интеграционных проектах их можно использовать почти где угодно и когда угодно. Во многих случаях вы даже можете расширить встроенную функциональность Camel с помощью своего кода.

    Простота конфигурирования


    Конфигурирование в Camel сведено к минимуму везде, где только можно. Чтобы задавать конечные точки непосредственно в маршрутах, используется простая и понятная конфигурация URI.

    Например, вы можете настроить маршрут Camel, который будет начинаться от файловой конечной точки и рекурсивно сканировать вложенные папки на предмет файлов .txt, всего лишь написав следующее:

    1	from("file:data/inbox?recursive=true&include=.*txt$")...
    

    Автоматические конверторы типов


    В Camel есть встроенный механизм преобразования типов, имеющий более 350 конверторов. Вам больше на надо конфигурировать правила конвертирования, например, чтобы преобразовать массив байтов в строку. И даже если в Camel нет нужного конвертора, вы всегда можете написать его сами. А самое приятное, что все это работает автономно, без вашего участия.

    Компоненты Camel тоже используют автоконвертирование типов, благодаря чему могут принимать большинство типов данных и преобразовывать их в тот тип, который способны использовать. В сообществе Camel эта функция входи в топ любимых, и кое-кто даже задается вопросом, почему ее нет в Java.

    Легковесное ядро, пригодное для встраивания в микросервисы


    Ядро Camel действительно весит совсем немного: размер библиотеки составляет около 4,9 МБ, еще около 1,3 МБ занимают runtime-зависимости. Поэтому Camel легко встраивается куда угодно и развертывается где угодно, включая автономные приложения, микросервисы, веб-приложения, приложения Spring, приложения Java EE, OSGi, Spring Boot, WildFly, а также облачные платформы, такие как AWS, Kubernetes или Cloud Foundry. Camel изначально создавался не как серверная система или решение ESB, а как то, что можно встраивать в любые исполняемые вещи, где есть Java.

    Поддержка облачных технологий


    Camel не только ориентирован на облачное использование, но и предлагает множество компонентов для подключения к облачным сервисам, таким как:

    • Amazon DynamoDB, EC2, Kinesis, SimpleDB, SES, SNS, SQS, SWF и S3
    • Braintree (PayPal, Apple, Android Pay, и т. п.)
    • Dropbox
    • Facebook
    • GitHub
    • Google Big Query, Calendar, Drive, Mail и Pub Sub
    • HipChat
    • LinkedIn
    • Salesforce
    • Twitter
    • и многим другим

    Пакет средств тестирования


    В состав Camel входит пакет для тестирования приложений. Этот пакет активно используется и для тестирования самого Camel и насчитывает более 18 тыс. модульных тестов. Пакет содержит специальные компоненты, позволяющие, например, сымитировать реальные конечные точки или задать ожидаемый результат, чтобы Camel мог проверить, отвечает ли приложение заданным требованиям или нет.

    Активное сообщество разработки


    У проекта Camel активное и деятельное сообщество, которое на момент написания этой книги живет и развивается уже более 10 лет. Наличие такого сообщества – это важный фактор при выборе ПО с открытым кодом для построения собственных приложений. Если проект неактивен, вы вряд ли можете рассчитывать на поддержку сообщества и остаетесь со своими проблемами один на один. И наоборот, участники активного проекта, такого как Camel, причем, как разработчики, так и простые пользователи, всегда готовы прийти на помощь.

    Теперь, когда мы вкратце рассмотрели основные особенности Camel, пора перейти к практике: получить его дистрибутив и рассмотреть несколько примеров.

    Приступая к работе


    В этом разделе мы расскажем, как получить дистрибутив Camel, и вкратце опишем его содержимое. Затем мы покажем, как выполнять приведенные в этой книге примеры исходного кода с использованием Apache Maven.

    Начнем с дистрибутива.

    Загрузка и установки Camel


    Camel доступен на сайте проекта Apache Camel. Там вы найдете список всех версий и ссылки для загрузки последнего релиза Camel.

    В этой книге мы используем Camel 2.20.1. Чтобы получить эту версию, воспользуйтесь соответствующей ссылкой на странице загрузки. В конце этой страницы есть ссылки на два дистрибутива: zip-файл для Windows и tar.gz для macOS/Linux. Загрузите нужный файл и распакуйте его на локальный диск.

    Откройте консоль и перейдите в папку, куда вы распаковали дистрибутив Camel. В ней будут следующие файлы:

    1	[janstey@ghost apache-camel-2.20.1]$ ls
    2	doc  examples  lib  LICENSE.txt  NOTICE.txt  README.txt
    


    Как видите, дистрибутив имеет простую и понятную структуру, а именно:

    • doc – папка с руководством по Camel в формате HTML. Это просто копия сайта Camel на момент выпуска релиза. Она пригодится в те моменты, когда у вас нет доступа к сайту или нет под рукой этой книги
    • examples – папка с 97 примерами Camel
    • lib – папка со всеми библиотеками Camel. Позже мы покажем, как использовать Maven для простой загрузки зависимостей для компонентов, не входящих в ядро
    • LICENSE.txt – файл с текстом лицензии на дистрибутив Camel. Поскольку это проект Apache, лицензия представляет собой Apache License, version 2.0
    • NOTICE.txt – файл с информацией о авторских правах на сторонние зависимости, входящие в состав дистрибутива Camel
    • README.txt – файл с кратким введением в Camel и перечнем ссылок, которые помогут новичкам быстро приступить к работе

    Теперь разберемся, как выполнять приведенные в этой книге примеры Camel.

    Первый запуск Camel


    Итак, выше мы рассказали, как получить дистрибутив Camel, и вкратце описали его содержимое. Среди прочего там есть и примеры с пояснениями и инструкциями, которые мы советуем изучить.

    Однако в этой книге мы вообще не будем использовать дистрибутив. Все приведенные здесь примеры исходного кода задействуют Apache Maven, который автоматически загружает все необходимые библиотеки Camel. Поэтому вам не нужно будет контролировать, чтобы они прописывались в classpath.

    Все примеры кода из этой книги можно найти на сайте GitHub.

    Первый пример, который мы рассмотрим – это такой «hello world» в реалиях интеграции, иначе говоря, это маршрутизация файлов. Допустим, надо прочитать файлы в одной папке (data/inbox), обработать их определенным образом и записать результаты в другую папку (data/outbox). Для простоты опустим обработку, и тогда задача сведется к копированию исходных файлов, как показано на Рис. 1.4.

    Рис. 1.4 Маршрутизация файлов из папки data/inbox в папку data/outbox.


    Проще некуда, верно? Вот как выглядит реализация этой задачи на «чистой» Java, без использования Camel.

    Листинг 1.1 Маршрутизация файлов из одной папки в другую на «чистой» Java

    1	import java.io.File;
    2	import java.io.FileInputStream;
    3	import java.io.FileOutputStream;
    4	import java.io.IOException;
    5	import java.io.OutputStream;
    6	
    7	public class FileCopier {
    8	
    9	    public static void main(String args[]) throws Exception {
    10	        File inboxDirectory = new File("data/inbox");
    11	        File outboxDirectory = new File("data/outbox");
    12	        outboxDirectory.mkdir();
    13	        File[] files = inboxDirectory.listFiles();
    14	        for (File source : files) {
    15	            if (source.isFile()) {
    16	               File dest = new File(
    17	                    outboxDirectory.getPath()
    18	                    + File.separator
    19	                    + source.getName());
    20	               copyFile(source, dest);
    21	            }
    22	        }
    23	    }
    24	
    25	    private static void copyFile(File source, File dest)
    26	        throws IOException {
    27	        OutputStream out = new FileOutputStream(dest);
    28	        byte[] buffer = new byte[(int) source.length()];
    29	        FileInputStream in = new FileInputStream(source);
    30	        in.read(buffer);
    31	        try {
    32	            out.write(buffer);
    33	        } finally {
    34	            out.close();
    35	            in.close();
    36	        }
    37	    }
    38	}
    

    Казалось бы, совсем простая задача, но в FileCopier уже целых 37 строк кода. К тому же, пришлось использовать низкоуровневые файловые API, и – что важно – надо было не забыть правильно закрыть ресурсы. Теперь представим, что копировать из папки data/inbox надо только новые файлы. Тогда потребуется установить таймер и отслеживать, какие файлы уже были скопированы. Сложность растет прямо на глазах.

    К счастью, интеграционные задачи, наподобие только что рассмотренной, уже решены на тысячу раз. Поэтому не нужно изобретать велосипед и писать код руками. Давайте посмотрим, как весь этот функционал реализуется средствами фреймворка интеграции, такого как Apache Camel.

    Листинг 1.2 Маршрутизация файлов из одной папки в другую на Apache Camel

    1	import org.apache.camel.CamelContext;
    2	import org.apache.camel.builder.RouteBuilder;
    3	import org.apache.camel.impl.DefaultCamelContext;
    4	
    5	public class FileCopierWithCamel {
    6	
    7	    public static void main(String args[]) throws Exception {
    8	        CamelContext context = new DefaultCamelContext();
    9	        context.addRoutes(new RouteBuilder() {
    10	            public void configure() {
    11	                from("file:data/inbox?noop=true")     
    12	.to("file:data/outbox");                       
    13	            }
    14	        });
    15	        context.start();                         
    16	        Thread.sleep(10000);
    17	        context.stop();
    18	    }
    19	}
    

    Большинство кода в этом примере – шаблон. Любое приложение Camel использует CamelContext, который впоследствии запускается и затем останавливается. Мы также добавили метод sleep, чтобы дать приложению Camel время на копирование файлов. Главное, на что надо обратить внимание в листинге1.2 – маршрут.

    Маршруты в Camel определяются в том же порядке, в каком они читаются. Например, маршрут из примера выше можно прочесть следующим образом: принять сообщения из файла (from file) в папке data/inbox с параметром noop и отправить в файл (to file) в папке data/outbox. Параметр noop означает, что исходные файлы надо оставить на месте, иначе Camel перенесет их. Большинство людей, которые никогда не сталкивались с Camel, вполне могут понять, что делает этот маршрут. Кроме того, обратите внимание, что если отбросить шаблонный код, то вся задача сводится к двум строкам кода на Java.

    Для запуска этого примера потребуется загрузить и установить Apache Maven. После того, как он будет готов, откройте консоль и перейдите в папку chapter1/file-copy в каталоге с исходными текстами примеров из этой книги. В ней вы найдете:

    • data – в этой папке есть папка inbox с единственным файлом message1.xml
    • src – папка с исходным кодом примеров из этой главы
    • pom.xml – файл с информацией для сборки примеров, а именно XML-файл Maven POM (Project Object Model)

    ПРИМЕЧАНИЕ

    При написании это книги использовался Maven 3.5.0. Другие версии Maven могут работать не совсем так, как здесь показано.

    Содержимое файла POM приводится на листинге ниже.

    Листинг 1.3 Файл Maven POM, необходимый для использования основной библиотеки Camel

    1	<project xmlns="http://maven.apache.org/POM/4.0.0"
    2	  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3	  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    4	                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5	  <modelVersion>4.0.0</modelVersion>
    6	  <parent>
    7	    <groupId>com.camelinaction</groupId>     
    8	<artifactId>chapter1</artifactId>          
    9	    <version>2.0.0</version>                   
    10	  </parent>
    11	
    12	  <artifactId>chapter1-file-copy</artifactId>
    13	  <name>Camel in Action 2 :: Chapter 1 :: File Copy Example</name>
    14	
    15	  <dependencies>
    16	    <dependency>
    17	      <groupId>org.apache.camel</groupId>     
    18	<artifactId>camel-core</artifactId>     
    19	    </dependency>
    20	    <dependency>
    21	      <groupId>org.slf4j</groupId>     
    22	<artifactId>slf4j-log4j12</artifactId>     
    23	    </dependency>                              
    24	  </dependencies>
    25	</project>
    

    Maven – это отдельная большая тема, и здесь мы коснемся ее по самому минимуму лишь для того, чтобы вы могли работать с примерами.

    POM-файл Maven на Листинге 1.3 – это, возможно, один из самых коротких POM-файлов в вашей жизни, поскольку в нем используются практически все настройки POM по умолчанию. Помимо них, некоторые настройки задаются в родительском (parent) POM. Пожалуй, самая важная часть здесь – это зависимость от библиотеки Camel. Используя этот POM, Maven делает следующее:

    1. Создает search path на основе groupId, artifactId и version. Для элемента version задано свойство camel-version, определенное в POM, на который ссылается элемент parent, и равное в итоге 2.20.1. Тип зависимости не задан, поэтому считается, что это файл JAR. Search path таким образом превращается в org/apache/camel/camel-core/2.20.1/camel-core-2.20.1.jar.
    2. Поскольку листинг 1.3 не содержит никаких указаний, где искать зависимости Camel, Maven ищет их в своем центральном репозитории по адресу.
    3. Объединив search path и URL репозитория, Maven пытается загрузить файл.
    4. JAR-файл сохраняется в локальный кэш Maven, который обычно находится в домашнем каталоге .m2/repository. В Linux/macOS это папка ~/.m2/repository, в Windows – C:\Users\[Username]\.m2\repository.
    5. При запуске кода приложения из листинга 1.2 JAR-файл Camel добавляется в classpath.

    Чтобы запустить пример из листинга 1.2, перейдите в папку chapter1/file-copy и выполните следующую команду:

    1	mvn compile exec:java
    


    В результате Maven скомпилирует исходник в каталоге src и выполнит класс FileCopierWithCamel с JAR-файлом camel-core в classpath.

    ПРИМЕЧАНИЕ

    Все примеры в этой книге рассчитаны на то, что вы подключены к интернету, поскольку Apache Maven будет активно загружать JAR-зависимости. Общий размер скачанных по итогам выполнения всех примеров из этой книги библиотек составит несколько сот мегабайт.

    Запустите команду Maven из папки chapter1/file-copy и после ее завершения перейдите в папку data/outbox, чтобы убедиться, что файл скопировался. Поздравляем, вы только что выполнили свой первый пример. Да, он очень простой, но теперь вы знаете, как запускать почти все остальные примеры из этой книги.

    </конец перевода>
    Red Hat
    76,00
    Программные решения с открытым исходным кодом
    Поделиться публикацией

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

      0
      Вопрос касательно микросервисной архитектуры.

      Всем известно, что у фреймворка SpringBoot существует своя экосистема для построения микросервисов. Также, не секрет, известно, что проектов, построенные на Apache Camel, хорошо работают под управлением контейнеров Apache Karaf.

      Мне интересно, есть ли возможность реализации проекта с интерфейсом для пользователя, написанном на SpringBoot, бизнес-логика которого использует Apache Camel в качестве маршрутизатора потока информации, в виде бандла (bundle) для Apache Karaf? Запустится ли SpringBoot внутри Karaf-контейнера? Или следует использовать экосистему SpringBoot-а для этих целей, добавляя в нее модуль Apache Camel? Или, возможно, нужно их держать отдельно, связывая посредством Message-брокеров (ActiveMQ) и баз данных?
        0
        Зачем вам spring boot внутри карафа? Это практически лишено смысла.
          0
          Да, немного почитав, я, что не нужно.
        0
        Вопрос по работе с бинами в роутах.
        Начну с описания проблемы, которую нужно решить.
        В процессе обработки сообщения нужно выполнить определённую бизнес-логику. Бизнес-логика инкапсулирована в бине. В качестве di-контейнера используется spring.
        Сделать это можно, например, так:
        .bean("myBeanName")

        Далее, в контейнере хранится несколько объектов одного класса, отличающиеся названием бина ну и, соответственно, преобразованием сообщения. В сообщении в свойствах хранится название бина, который должен его обработать.
        Теперь суть вопроса: как в роуте указать, какой именно бин нужно выполнить?
        Т.е. что-то типа такого:
        .bean("${exchangeProperty[propertyName].getMyBeanName()}")

        Т.о. имеем роут, который динамически получает из сообщения название бина и выполняет его. При получении разных сообщений выполняем разные бины.
          0
          .bean(exchangeProperty[propertyName].getMyBeanName())

          а так не сработает?
            0
            Нет, exchangeProperty возвращает ValueBuilder. Я его не могу привести к кастомному типу.
            Вообще, я нашёл способ выполнить бин по названию из свойств самого сообщения:
            .routingSlip(simple("bean:${exchangeProperty[propertyName].getMyBeanName()}"))

            Но есть 2 проблемы.
            1. По-моему, костыльно. Я думаю, должен быть какой-то «правильный» способ получить название бина из свойств сообщения.
            2. Один из роутов мне нужно выполнить с транзакцией:
            .transacted("customTransactionPolicy")

            , где customTransactionPolicy — имя кастомного бина, который так же меняется в зависимости от того, какое сообщение пришло в роут.
            Вот здесь мне не удалось никак подставить нужный бин.
              0
              Я создал тестовый проект из мавен-архетипа camel-archetype-spring-boot, и сделал вот что:

              @Component
              public class TestBean1 {
              
                  @Handler
                  public void test1(Exchange exchange) {
                      System.out.println("TEST 1");
                  }
              
              }
              
              @Component
              public class MySpringBootRouter extends RouteBuilder {
              
                  @Override
                  public void configure() {
              
                      Object bean1 = getContext().getRegistry().lookupByName("testBean1");
              
                      from("timer:trigger")
                              .bean(bean1)
                              .transform().simple("ref:myBean")
                              .to("log:out");
                  }
              
                  @Bean
                  String myBean() {
                      return "I'm Spring bean!";
                  }
              
              }
              
              


              и получаю в консоль вот такое:

              TEST 1
              2018-04-02 15:27:56.643 INFO 15716 --- [timer://trigger] out : Exchange[ExchangePattern: InOnly, BodyType: String, Body: I'm Spring bean!]
              TEST 1
              2018-04-02 15:27:57.624 INFO 15716 --- [timer://trigger] out : Exchange[ExchangePattern: InOnly, BodyType: String, Body: I'm Spring bean!]
              TEST 1
              2018-04-02 15:27:58.624 INFO 15716 --- [timer://trigger] out : Exchange[ExchangePattern: InOnly, BodyType: String, Body: I'm Spring bean!]
              TEST 1
              2018-04-02 15:27:59.623 INFO 15716 --- [timer://trigger] out : Exchange[ExchangePattern: InOnly, BodyType: String, Body: I'm Spring bean!]
              TEST 1
              2018-04-02 15:28:00.625 INFO 15716 --- [timer://trigger] out : Exchange[ExchangePattern: InOnly, BodyType: String, Body: I'm Spring bean!]



              Попробуйте использовать конструкцию
              getContext().getRegistry().lookupByName("testBean1");
              для получения конкретного бина по имени.
                0
                В вашем примере на момент загрузки роута уже известно имя бина — «testBean1»:
                Object bean1 = getContext().getRegistry().lookupByName("testBean1");
                

                По идее, в этом случае роут должен отработать правильно и если просто указать:
                .bean("testBean1")
                


                В случае с динамическим получением бина проблема будет выглядеть так:
                Есть 2 объекта — MasterObject1 и MasterObject2, реализующих общий интерфейс.

                // Первый основной роут 
                fromF("timer://mainRoute1?period=5s")
                     // устанавливаем в свойство "master" объект, который содержит имя целевого бина  
                     .process(exchange -> exchange.setProperty("master", MasterObject1))
                     .toF("direct:actionRoute")
                ;
                
                // Второй основной роут 
                fromF("timer://mainRoute2?period=5s")
                     .process(exchange -> exchange.setProperty("master", MasterObject2))
                     .toF("direct:actionRoute")
                ;
                
                // Роут, в котором нужно выполнить бизнес-логику, инкапсулированную в конкретном бине объекта 
                fromF("direct:actionRoute")
                     // а здесь пробуем получить бин из объекта, который ранее положили в свойство
                     .bean("${exchangeProperty[master].getMyBeanName()}")
                ;
                
                


                Вот проблема именно в этом месте:
                .bean("${exchangeProperty[master].getMyBeanName()}")

                Имя бина неизвестно на момент загрузки роута.
                  0
                  Кажется, я нашел решение для вашего случая. Описано здесь.

                  Вот тест:

                  @Component
                  public class BeanResolver {
                      public String routeMe(String body, Exchange exchange) {
                          return "bean:"+exchange.getProperty("master");
                      }
                  }
                  
                  @Component
                  public class MasterObject1 {
                  
                      private static final Logger logger = LoggerFactory.getLogger(MasterObject1.class);
                  
                      @Handler
                      public void handler(Exchange exchange) {
                          logger.warn("HELLO FROM "+this.getClass().getName());
                      }
                  }
                  
                  @Component
                  public class MySpringBootRouter extends RouteBuilder {
                  
                      @Override
                      public void configure() {
                  
                          from("timer:trigger")
                                  .process(exchange -> exchange.setProperty("master", "masterObject1"))
                                  .dynamicRouter(method(BeanResolver.class, "routeMe"));
                      }
                  
                  }
                  
                  


                  Выхлоп:

                  2018-04-03 10:39:07.695 WARN 3228 --- [timer://trigger] ru.bvn13.test.MasterObject1 : HELLO FROM ru.bvn13.test.MasterObject1
                  2018-04-03 10:39:07.695 WARN 3228 --- [timer://trigger] ru.bvn13.test.MasterObject1 : HELLO FROM ru.bvn13.test.MasterObject1
                    0
                    Да, это может быть возможным решением, если код немного доработать. Класс BeanResolver будет выглядеть так:
                    @Component
                    public class BeanResolver {
                        private static Boolean EXECUTED = false;
                    
                        public String execute(Exchange exchange) {
                            if (EXECUTED) {
                                EXECUTED = false;
                                return null;
                            } else {
                                EXECUTED = true;
                                return "bean:" + ((Agent) exchange.getProperty("master")).getBean();
                            }
                        }
                    }


                    Здесь проблема в том, что элемент .dynamicRouter() выполняется циклично до тех пор, пока выражение для определения бина не вернёт null.
                    camel.apache.org/dynamic-router.html
                    В данном случае придётся запилить статическую переменную, которая будет следить за тем, чтобы роут выполнился только один раз.

                    В данном случае проще воспользоваться тем примером, что я скидывал выше:
                    .routingSlip(simple("bean:${exchangeProperty[master].getBean()}"))
                    

                    Писать меньше, но всё-равно костыльно.

                    Но оба решения у меня не получилось использовать для .transacted(«myBeanName»)
                    Этот элемент принимает только название бина в виде строки. Я пробовал передавать метод, который возвращает строку, но не могу получить объект exchange в методе.
                      0
                      Но всё-равно большое спасибо за советы. Я просто предполагал, что должен быть какой-то простой способ получения бина динамически, т.к. мне это не кажется какой-то экзотической задачей.
                        0
                        Я тут поразмыслил над вашим кейсом. Вряд ли у вас возможен случай, когда вы не можете достоверно определить бин. В том смысле, что название бина составляется в рантайме. Скорее всего, выбор все-таки из конечного числа бинов. И если добавляется новый обработчик-бин, то нужно его прописать и перекомпилировать. В таком случае вам подойдет вариант с Router-ом. Т.е. вы добавляете бин, прописываете проперти и добавляете условие по которому этот бин «сработает».

                        from("timer:trigger")
                            .process(exchange -> exchange.setProperty("master", "masterObject1"))
                            .choice()
                                 .when(exchange -> exchange.getProperty("master").equals("masterObject1"))
                                 .bean("masterObject1")
                                 .otherwise().log("NONE")
                        
                        
            0
            Задавайте вопросы Клаусу в комментариях к этому тексту, и автор самого интересного вопроса (которого мы с Клаусом выберем уже 2 апреля) получит от нас и наших друзей из Jug.ru билет на конференцию и сможет не только услышать Клауса и других участников вживую, но и принять участие в розыгрыше книги «Camel in Action».

            Уже не будет розыгрыша? или это и был розыгрыш?
              0
              Здравствуйте! Как раз показываем Клаусу вопросы, к вечеру отпишемся

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое