Статьи

Разработка на основе шаблонов с ServiceMix

Вы все еще работаете с интеграционными продуктами одного из типичных крупных интеграционных вендоров, таких как IBM и Tibco, и ищете более легкий подход? Затем эти две статьи о Mule и Apache ServiceMix могут послужить откровением для потенциала ESB с открытым исходным кодом. Если вы уже знакомы с Mule или Apache ServiceMix, эти статьи предоставят некоторое представление о новых возможностях последних версий этих ESB с открытым исходным кодом и покажут, как подходить к проектированию вашего интеграционного решения.

В этих двух статьях также будет предпринята попытка демистифицировать обсуждение между реализацией ESB на основе Java Business Integration (JBI) (Apache ServiceMix) и разработанной на заказ реализацией ESB (Mule). В этих двух статьях мы реализуем интеграционное решение с Mule и Apache ServiceMix, чтобы показать, что каждый из этих подходов имеет свои преимущества и что между этими двумя подходами действительно есть частичное совпадение.

В первой статье мы обсуждали Мул. В этой второй статье мы сосредоточимся на JBI и ServiceMix, ServiceMix 3.2.1, чтобы быть более конкретным, который является последним производственным выпуском проекта ServiceMix.

Краткое введение в JBI

Чтобы понять архитектуру ServiceMix, нам сначала нужно немного обсудить спецификацию JBI. JBI определяет основанную на стандартах архитектуру, которую можно использовать в качестве основы для продуктов на основе Java, в частности ESB. Помимо ServiceMix есть несколько других ESB с открытым исходным кодом, основанных на этом стандарте, таких как OpenESB и Petals. Конечно, если вы хотите узнать больше, полную спецификацию можно найти на http://jcp.org/en/jsr/detail?id=208 . Несмотря на то, что это документ на 240 страниц, он очень хорошо написан и читаем.

В спецификации JBI есть несколько ключевых понятий. Существует два вида компонентов JBI: сервисный механизм (SE) и компонент связывания (BC), которые обеспечивают логику интеграции. BC предоставляют функциональные возможности связи для отправки сообщений в контейнер JBI и из него, как, например, JMS BC, File BC и HTTP BC. SE предоставляют функциональные возможности интеграции, которые могут использоваться для построения ваших интеграционных решений, таких как маршрутизация, преобразование и логика маршрутизации. BC и SE связываются друг с другом через нормализованный маршрутизатор сообщений (NMR). При наличии ЯМР BC и SE разъединяются, и связь осуществляется с помощью так называемых нормализованных сообщений. Уровень связи между BCs и SE и ЯМР реализован с помощью канала доставки (DC).DC является двунаправленным каналом связи, который предоставляет контракт API для BC и SE, чтобы иметь возможность отправлять и получать сообщения от NMR. Обзор этих основополагающих концепций JBI представлен на рисунке 1.

Рисунок 1 Обзор важных концепций JBI и их взаимосвязи.

Инфраструктура JBI, показанная на рисунке 1, — это именно то, что предоставляет Apache ServiceMix. ServiceMix предоставляет контейнер JBI, который реализует ЯМР, нормализованное сообщение и DC и, что более важно для разработчика интеграции, предоставляет множество BC и SE. Несколько из этих BC и SE показаны на рисунке 2.

Рис. 2. Обзор архитектуры Apache ServiceMix, показывающий ряд компонентов связывания и сервисных механизмов, предоставляемых «из коробки».

Теперь мы обсудили теорию, лежащую в основе JBI, и показали обзор Apache ServiceMix на рисунке 2, пришло время реально что-то реализовать. Но что мы должны реализовать, чтобы интегрированное решение работало на ServiceMix? Что ж, мы должны создать сервисные единицы (SU) и сервисную сборку (SA). Для каждого связующего компонента и сервисного механизма, которые вы хотите использовать в своем интеграционном решении, вам нужно будет создать сервисный блок. Созданные сервисные единицы могут быть упакованы как сервисная сборка, которую можно развернуть в контейнере JBI, таком как ServiceMix. SU — это просто файл JAR с файлом конфигурации для конкретной BC или SE. И SA — это также просто JAR-файл с дескриптором развертывания JBI с именем jbi.xml. Но давайте прекратим говорить и реализуем простой поток сообщений для ServiceMix.

Создайте поток сообщений ServiceMix 3.2.1

В первой статье о Mule мы представили пример hello world, демонстрирующий основы конфигурации Mule. Итак, давайте реализуем тот же пример с ServiceMix. Сначала нам нужно выбрать BC и SE, которые мы будем использовать при реализации примера hello world. Во-первых, нам нужно JMS-соединение для получения и отправки сообщений в контейнере ServiceMix, поэтому нам нужен JMS BC.

Затем нам нужно реализовать простой Java-бин, который возвращает параметр входного имени с введенным префиксом. Мы можем реализовать эту функциональность как минимум с двумя SE: SE Bean и JSR181 SE. В этом примере мы будем использовать JSR181 SE, потому что этот SE обеспечивает хороший уровень абстракции от JBI и контейнера ServiceMix, в то время как Bean SE обеспечивает функциональность для более сложных вариантов использования. На рисунке 3 представлен обзор SU, которые нам нужно создать для привет-мира SA.

Рис. 3. Обзор сервисных модулей, которые нам нужно реализовать на примере Hello World.

Мы реализуем потребитель JMS с конфигурацией JMS BC и HelloComponent конфигурацией и реализацией с JSR181 SE. Конфигурация в ServiceMix реализуется с помощью платформы Apache XBean, которая является подпроектом проекта Apache Geronimo. Платформа XBean позволяет легко определить собственный язык конфигурации с помощью встроенной поддержки, например, Spring. Конфигурация SU в ServiceMix реализуется с помощью файла xbean.xml , в котором используется набор элементов и атрибутов, специфичных для BC или SE. В листинге 1 показана конфигурация для JMS BC, которая использует конфигурацию XBean JMS BC.

Конфигурация JMS перечисления 1 для простого примера в ServiceMix

<beans xmlns:jms="http://servicemix.apache.org/jms/1.0"
xmlns:esb="http://esbinaction.com/simple">

<jms:consumer service="esb:simpleReceiver"
endpoint="jmsEndpoint"
targetService="esb:helloComponent"
destinationName="in-queue"
replyDestinationName="out-queue"
connectionFactory="#connectionFactory"
marshaler="#replyInOutMarshaler"/>

<bean id="replyInOutMarshaler"
class="org.apache.servicemix.jms.endpoints.DefaultConsumerMarshaler">
<property name="mep" value="http://www.w3.org/2004/08/wsdl/in-out" />
</bean>

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
</beans>

Конфигурация JMS BC использует корневой элемент bean , который ссылается на язык конфигурации Spring. Как видите, пространство имен ServiceMix JMS BC объявлено на корневом уровне. Декларация потребителя JMS, которая использует пространство имен JMS BC, настраивает большую часть необходимой нам функциональности JMS. DestinationName и replyDestinationName атрибутов сказать JMS BC , чтобы соответственно прослушивать сообщения о в очереди и отправленные сообщения ответа на выходе очередь .

На первый взгляд может показаться немного странным, что мы также настраиваем адрес получателя ответа в потребителе JMS. Этот выбор связан с использованием шаблонов обмена сообщениями (MEP) в JBI и ServiceMix. Для каждого сообщения, используемого контейнером ServiceMix, будет создан обмен сообщениями с определенным MEP. Концепция MEP введена в спецификации WSDL, а поддерживаемые MEP в JBI являются входящими-выходными-только-входными, надежными-только-дополнительными и необязательными-только-только. Конечно, наиболее распространенные MEP являются входными и выходными, и эти MEP также связаны с нашим выбором конфигурации JMS BC.

По умолчанию потребитель JMS использует только MEP, поэтому это означает, что сообщение JMS будет получено из очереди и отправлено в контейнере ServiceMix. Но Java-бин JSR181 hello возвращает ответ, и для единственного MEP не может быть ответного сообщения. Таким образом, нам нужен какой-то способ преобразовать входную MEP в входную MEP или использовать входную MEP в потребителе JMS. Обзор этого решения показан на рисунке 4.

Рисунок 4. Обзор выбора в MEP и использования BC и SE для реализации JMS.

Очевидно, что в листинге 1 мы выбрали второй вариант, показанный на рисунке 4. Мы настроили потребителя JMS на использование входного MEP с DefaultConsumerMarshaler и свойством mep . Это означает, что использованное сообщение JMS отправляется HelloComponent, а потребитель JMS ожидает ответа от этого Java-компонента. Возвращенное сообщение затем отправляется адресату ответа, который в данном примере является out-queue .

Альтернативная реализация могла бы использовать стандартную MEP in-only в приемнике JMS и преобразовать обмен сообщениями в MEP in-out с конвейером EIP SE. Конвейер может использоваться для преобразования входной MEP в входной MEP. Ответ HelloComponentотправляется поставщику JMS, также настроенному в конфигурации конвейера EIP. Пример конфигурации конвейера показан в листинге 2. Пример конфигурации конвейера EIP в

листинге 2

<beans xmlns:eip="http://servicemix.apache.org/eip/1.0"
xmlns:esb="http://esbinaction.com/simple">

<eip:pipeline service="esb:examplePipeline"
endpoint="routingEndpoint">
<eip:transformer>
<eip:exchange-target service="esb:helloComponent" />
</eip:transformer>
<eip:target>
<eip:exchange-target service="esb:simpleSender" />
</eip:target>
</eip:pipeline>
</beans>

В листинге 2 показано определение элемента конвейера с дочерними элементами преобразователя и цели. Элемент преобразователя определяет цель обмена входами-выходами , в этом примере HelloComponent и целевой элемент определяют цель для ответного сообщения обмена входами-выходами , которое будет отправлено как MEP только для входа. Поэтому для реализации интеграционных решений в контейнере JBI, таком как ServiceMix, необходимо хорошее понимание MEP. Но с обзором, описанным здесь, вы будете готовы начать использовать ServiceMix для более сложных случаев интеграции.

Обратите внимание, что в листингах 1 и 2 мы определили service , endpoint и targetService.атрибутов. Эти атрибуты очень важны в контейнере ServiceMix, потому что они определяют уникальную идентификацию для конфигурации компонента в контейнере ServiceMix. Таким образом, атрибуты службы и конечной точки однозначно определяют потребителя JMS и компоненты конвейера EIP в контейнере ServiceMix. И чтобы вызвать поставщика услуг в контейнере ServiceMix, мы можем определить targetService и необязательное значение targetEndpoint, которое соответствует определению службы и конечной точки этого поставщика услуг. Например, в листинге 1 мы определили атрибут targetService со значением esb: helloComponent, которая должна соответствовать конфигурации сервиса Java bean-компонента JSR181 SE . Обратите внимание, что определением службы всегда является пространство имен, указанное в ServiceMix.

Теперь давайте посмотрим на конфигурацию JSR181 SE в листинге 3 и посмотреть , если сервис определения соответствует targetService конфигурации листинга 1.

конфигурации Листинг 3 XBean боба JSR181 привет Java

<beans xmlns:jsr181="http://servicemix.apache.org/jsr181/1.0"
xmlns:esb="http://esbinaction.com/simple">

<jsr181:endpoint annotations="none" service="esb:helloComponent" endpoint="endpoint"
serviceInterface="esb.example.HelloIF" id="hello">
<jsr181:pojo>
<bean id="helloBean" class="esb.example.HelloBean">
<property name="prefix" value="Hello "/>
</bean>
</jsr181:pojo>
</jsr181:endpoint>
</beans>

Обратите внимание, что конфигурация JSR181 SE использует другое пространство имен в качестве примеров листингов 1 и 2 JMS BC и EIP SE. Определение конечной точки JSR181, окружающее конфигурацию HelloBean, на самом деле является просто оболочкой для представления HelloBean в контейнере ServiceMix со службой и конечной точкой определение. JSR181 SE использует платформу XFire для предоставления функциональных возможностей для сериализации и десериализации сообщений XML в бины Java и из них. Поскольку в контейнере ServiceMix каждое сообщение является XML, нам нужен какой-то способ сериализации этой Java и обратно. Это именно то, что обеспечивает JSR181 SE без дополнительных настроек.

Чтобы завершить, листинг 4 показывает реализацию HelloIFинтерфейс и класс HelloBean Java.

Перечисление 4 HelloBean и HelloIF, которое используется в конфигурации SE JSR181

public interface HelloIF {
public String hello(String name);
}

public class HelloBean {

private String prefix;

public String hello(String name) {
return prefix + name;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}
}

Теперь мы создали конфигурации сервисного блока для JMS BC и JSR181 SE, последняя оставшаяся часть — это конфигурация SA. Контейнеру JBI требуется дескриптор развертывания (jbi.xml), который описывает сервисные единицы, включенные в SA. В листинге 5 показана конфигурация jbi.xml для hello world SA.

Перечисление 5 JBI-дескриптор развертывания, jbi.xml, для примера hello world

<jbi xmlns="http://java.sun.com/xml/ns/jbi" version="1.0">
<service-assembly>
<identification>
<name>simple-sa</name>
</identification>
<service-unit>
<identification>
<name>simple-jms-su</name>
</identification>
<target>
<artifacts-zip>simple-jms-su.zip</artifacts-zip>
<component-name>servicemix-jms</component-name>
</target>
</service-unit>
<service-unit>
<identification>
<name>simple-jsr181-su</name>
</identification>
<target>
<artifacts-zip>simple-jsr181-su.zip</artifacts-zip>
<component-name>servicemix-jsr181</component-name>
</target>
</service-unit>
</service-assembly>
</jbi>

Теперь мы определили все части примера hello world, поэтому теперь можем создавать файлы SU JAR и файл SA JAR. В проект SimpleSMDZone, включенный в исходный код этой статьи, включен файл сборки Ant, который создает эти файлы JAR. А проект ServiceMix предоставляет цели Ant для развертывания SA в контейнере ServiceMix. Чтобы начать пример, вы можете запустить цель запуска файла сборки build.xml Ant, чтобы запустить контейнер ServiceMix. Затем запустите цель deploy-simple для развертывания hello world SA.

Этот пример легко проверить с помощью класса JUnit, который отправляет сообщение JMS в очередь в очереди и прослушивает ответное сообщение в очереди, Класс JUnit также предоставляется в проекте SimpleSMDZone . Теперь давайте рассмотрим более сложный пример — тематическое исследование страхового брокера, которое мы также реализовали в первой статье о Муле.

Пример из практики: страховой брокер

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

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

Рисунок 5 Обзор тематического исследования страхового брокера, показывающий сообщения, отправляемые поставщикам страхования в зависимости от типа запроса на страхование

Еще одна проблема, которую нам необходимо решить в нашем интеграционном решении, заключается в том, что страховой компании budgetCar требуется отправка запросов в формате CSV через FTP (для простоты здесь мы будем использовать файловый коннектор), а для страхования LuxuryCar требуется XML файл через JMS. Компания «Страхование путешествий курорта» требует, чтобы все звонки осуществлялись с использованием веб-сервисов.

Шаблонный подход к дизайну

Большинство людей, которые работали в интеграционных проектах, вероятно, знакомы с книгой Грегора Хоупа и Бобби Вульфа «Шаблоны интеграции предприятия» (EIP). В этой книге показан ряд шаблонов проектирования, которые вы можете использовать для документирования, описания и решения ваших проблем интеграции. В следующих нескольких параграфах мы покажем, как вы можете использовать шаблоны, описанные в книге EIP, для описания тематического исследования страхового брокера.

Давайте сначала посмотрим на рисунок 6, который описывает часть запроса для случая, который мы представили.

Рисунок 6 На этом рисунке показан поток запросов решения по интеграции страхового брокера, описанного с использованием шаблонов корпоративной интеграции из книги EIP.

Давайте сначала посмотрим на различные шаблоны, показанные здесь, прежде чем объяснить, как они работают вместе.

Таблица 1. Используемые шаблоны для потока запросов страхового примера.

Шаблон Описание
Канал сообщений [img_assist | nid = 3184 | title = | desc = | link = none | align = left | width = 55 | height = 37]   Канал сообщений позволяет приложениям связываться друг с другом.
Адаптер канала [img_assist | nid = 3179 | title = | desc = | link = none | align = middle | width = 46 | height = 31]   Адаптер канала определяет, как вы можете подключиться к системе обмена сообщениями (например, JMS-брокер), чтобы вы могли получать и отправлять сообщения.
Маршрутизатор на основе содержимого [img_assist | nid = 3186 | title = | desc = | link = none | align = middle | width = 54 | height = 37]   Поскольку имя подразумевает основанный на содержимом маршрутизатор, маршрутизирует сообщения на основе содержимого сообщения.

Список получателей
[img_assist | nid = 3185 | title = | desc = | link = none | align = middle | width = 54 | height = 37]

  Иногда вы хотите отправить сообщение нескольким каналам одновременно. Список получателей предусматривает в этом.

Трансформатор
[img_assist | nid = 3187 | title = | desc = | link = none | align = middle | width = 54 | height = 37]

  Преобразователь используется, когда необходимо изменить формат сообщения, прежде чем его можно будет отправить получателю, или когда определен стандартный формат.
Конечная точка сообщения [img_assist | nid = 3180 | title = | desc = | link = none | align = middle | width = 55 | height = 32]   Конечная точка сообщения определяет соединение между приложением и каналом обмена сообщениями.

 

Теперь давайте посмотрим, как эти шаблоны работают вместе, чтобы решить часть запроса нашего сайта страхования. Мы не будем показывать весь интерфейс, но начнем с запроса о страховке, полученного с веб-сайта EasyInsurance.

  1. Первое, что мы видим, это то, что веб-сайт использует «конечную точку сообщения» для отправки сообщения на определенный канал (в данном случае очередь JMS), которым управляет брокер.
  2. Эта очередь затем читается ESB с помощью «канального адаптера» JMS .
  3. Сообщение теперь маршрутизируется внутри ESB. Первый маршрутизатор — это «контентный маршрутизатор», который принимает сообщение и, в зависимости от типа запроса, автомобиля или поездки, направляет его к следующему компоненту.
  4. Если сообщение необходимо отправить нескольким страховым компаниям, используется список получателей, и перед тем, как сообщение действительно отправлено, оно сначала преобразуется в целевой формат сообщения с использованием «преобразователя» .
  5. «Трансформатор» уверил , что сообщение в формате получатель может работать, так что теперь все , что осталось сделать , это использовать другой «адаптер канала» , чтобы отправить сообщение в «канал сообщения» , и страховые компании может использовать «конечную точку сообщения» на своей стороне, чтобы прочитать запрос из «канала сообщений» .

Помимо использования корпоративных интеграционных шаблонов, поток запросов на рисунке 6 также показывает четкое разделение между различными логическими границами интеграционного решения. Конечно, ESB показан как логическая граница, но также брокер сообщений (поставщик JMS), веб-сайт и конечные точки страховых компаний разделены в разных границах. Также обратите внимание, что на рисунке 6 показан только поток запросов решения по изучению страхового случая. Чтобы сохранить дизайн чистым и понятным, часто лучше разделять потоки запросов и ответов в разных схемах проектирования. На рисунке 7 показан ответ интеграционного решения страхового брокера.

Рисунок 7 На этом рисунке показан поток ответов решения по интеграции страхования, описанного с использованием шаблонов интеграции предприятия из книги EIP.

Диаграмма схемы потока ответов, показанная на рисунке 7, содержит множество шаблонов, которые уже использовались при разработке потока запросов, здесь только шаблон агрегатора. Агрегатор используется для объединения сообщений о страховых ответах от страховых компаний BudgetCar и LuxuryCar в один ответ, который можно отобразить на веб-сайте EasyInsurance. Чтобы иметь возможность агрегировать ответные сообщения от двух компаний по страхованию автомобилей, нам нужен какой-то корреляционный идентификатор, который связывает эти ответные сообщения с исходным сообщением запроса на страхование. В этом примере мы используем идентификатор запроса в сообщении запроса страховки, которое также доступно в сообщении страхового ответа.

Это вводит еще один важный шаг на этапе разработки интеграционного решения: дизайн сообщения. Поскольку Mule также может использовать объекты Java в качестве типа полезной нагрузки сообщений, мы будем использовать сообщения на основе Java для связи между веб-сайтом EasyInsurance и брокером JMS и Enterprise Service Bus. В листинге 6 показаны элементы трех типов сообщений, задействованных в этом интеграционном решении.

Листинг 6 Обзор классов Java, представляющих запросы на страхование и ответные сообщения, используемые в реализации брокера интеграции.

public class CarInsuranceRequest implements Serializable {

private String requestID;
private String numberPlate;
private String carType;
private int buildYear;
private boolean companyCar;
private Date startDate;
}

public class TravelInsuranceRequest implements Serializable {

private String requestID;
private String destinationCountry;
private int numberOfPersons;
private Date startDate;
private Date endDate;
}

public class InsuranceResponse implements Serializable {
private String requestID;
private String responseID;
private String insuranceCompanyName;
private float price;
}

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

Обратите внимание, что пока мы еще не говорили о конкретных инструментах. В дизайне, который мы сделали, мы только что описали, что нужно сделать. Теперь мы можем передать поток интеграции и проектирование сообщения разработчику или специалисту по интеграции, которые могут реализовать его с использованием определенной технологии. В этой статье мы реализуем это сами и будем использовать ServiceMix 3.2.1 для этого.

Реализация страхового брокера с ServiceMix: часть запроса

При работе с JBI-контейнером, таким как ServiceMix, рекомендуется начать с диаграммы SA, прежде чем мы начнем с реализации нескольких файлов конфигурации BC и SE и классов реализации. С диаграммой SA, состоящей из SU, мы получаем хороший обзор того, что нам действительно нужно реализовать. Диаграмма SA части запроса примера страхового брокера показана на рисунке 8.

Рисунок 8 Обзор SA и SU для части запроса примера страхового брокера.

Как вы можете видеть на рисунке 8, нужно проделать довольно много работы. С цифрами в кругах показано упорядочение потока сообщений. Поток сообщений начинается с использования сообщения из очереди insurance.in с помощью JMS BC. Давайте посмотрим на конфигурацию JMS BC в листинге 7.

Листинг 7 Потребляем сообщение о страховке из очереди JMS

<beans xmlns:jms="http://servicemix.apache.org/jms/1.0"
xmlns:esb="http://esbinaction.com/insurance">

<classpath>
<location>.</location>
<location>bcel.jar</location>
<location>jibx-bind.jar</location>
<location>jibx-extras.jar</location>
<location>jibx-run.jar</location>
<location>qdox-1.6.1.jar</location>
<location>stax-api.jar</location>
<location>wstx-asl.jar</location>
<location>xmlpull_1_1_4.jar</location>
<location>xpp3.jar</location>
</classpath>

<jms:consumer service="esb:insuranceReceiver"
endpoint="jmsEndpoint"
targetService="esb:insuranceDSLRouter"
destinationName="insurance.in"
connectionFactory="#connectionFactory"
marshaler="#InsuranceJMSMarshaler"/>

<bean id="InsuranceJMSMarshaler"
class="esb.dzone.servicemix.util.InsuranceJMSMarshaler"/>

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>

</beans>

Конфигурация JMS BC, показанная в листинге 7, очень похожа на конфигурацию, которую мы видели ранее в примере hello world. Основное отличие состоит в том, что в этой реализации мы используем MEP только для чтения, а сообщение пересылается на маршрутизатор на основе содержимого, реализованный с помощью Apache Camel. Мы также определили маршалер в этой конфигурации, потому что мы отправили сообщение CarInsuranceRequest или TravelInsuranceRequest в очередь insurance.in . Маршалеру необходимо преобразовать эти сообщения в сообщение XML, прежде чем его можно будет отправить на маршрутизатор на основе содержимого Camel. Мы используем JiBX для преобразования объектов Java в XML и наоборот. Потому что мы используем JiBX в реализации InsuranceJMSMarshalerмы должны включить Jar JiBX в путь класса JMS BC, как показано в листинге 7. Реализация маршалера показана в листинге 8.

Листинг 8 Реализация маршалера JMS, который использует JiBX для преобразования XML.

public class InsuranceJMSMarshaler extends DefaultConsumerMarshaler {

protected void populateMessage(Message message,
NormalizedMessage normalizedMessage) throws Exception {
if (message instanceof ObjectMessage) {
ObjectMessage objectMessage = (ObjectMessage) message;
Object payload = objectMessage.getObject();
Source source = JiBXUtil.marshalDocument(payload, "UTF-8");
normalizedMessage.setContent(source);
} else {
throw new UnsupportedOperationException("JMS message is not a ObjectMessage");
}
}
}

Когда JMS ObjectMessage используется JMS BC, полезная нагрузка Java-объекта преобразуется в XML с помощью класса JiBXUtil . В исходном коде этой статьи вы можете найти более подробную информацию о преобразовании JiBX и реализации класса JiBXUtil .

Теперь давайте перейдем к реализации основанного на контенте маршрутизатора с Apache Camel. В дополнение к использованию EIP SE, проект ServiceMix рекомендует взглянуть на Camel SE для более сложных реализаций логики интеграции. Apache Camel — это подпроект проекта Apache ActiveMQ, в котором реализована большая часть корпоративных шаблонов интеграции из книги Hohpe and Woolf. Camel предоставляет конфигурацию на основе XML и язык, специфичный для домена Java (DSL), для реализации логики интеграции. Конфигурация Camel SE и реализация маршрутизатора на основе контента показана в листинге 9.

Листинг 9a. Отправьте запрос на страхование автомобиля двум службам страхования автомобиля.

<!-- camel-context.xml Camel SU configuration -->
<beans xmlns="http://www.springframework.org/schema/beans">
<camelContext id="camel" xmlns="http://activemq.apache.org/camel/schema/spring">
<package>esb.dzone.servicemix.camel</package>
</camelContext>
</beans>

Листинг 9б

/ The Camel content-based router implementation
public class InsuranceRouter extends RouteBuilder {
private final static String JBI_SERVICE = "jbi:service:";
private final static String NAMESPACE = "http://esbinaction.com/insurance";
private final static String SERVICE_IN = JBI_SERVICE + NAMESPACE +
"/insuranceDSLRouter";
private final static String LUXURY_CAR_OUT = JBI_SERVICE + NAMESPACE +
"/luxuryCarSender";
private final static String BUDGET_CAR_OUT = JBI_SERVICE + NAMESPACE +
"/budgetCarSender";
private final static String TRAVEL_OUT = JBI_SERVICE + NAMESPACE +
"/travelPipeline";
private final static String FAILURE_OUT = JBI_SERVICE + NAMESPACE +
"/insuranceFailureSender";

public void configure() {
from(SERVICE_IN)
.convertBodyTo(DOMSource.class)
.choice()
.when(xpath("//ins:TravelInsuranceRequest")
.namespace("ins", "http://dzone.com/insurance"))
.to(TRAVEL_OUT)
.when(xpath("//ins:CarInsuranceRequest")
.namespace("ins", "http://dzone.com/insurance"))
.to(LUXURY_CAR_OUT, BUDGET_CAR_OUT)
.otherwise()
.to(FAILURE_OUT);
}
}

Листинг 9 состоит из двух файлов: файла camel-context.xml, который используется для настройки SU Camel, и Java-класса InsuranceRouter, который реализует логику маршрутизации на основе содержимого. Верблюжья context.xml файл определяет пакет , который будет проверяться во время выполнения , чтобы искать классы Camel. Среда выполнения Camel найдет класс InsuranceRouter , поскольку он расширяет базовый класс RouteBuilder Camel.

В InsuranceRouter мы должны реализовать метод configure с помощью логики маршрутизации на основе содержимого. Во-первых, мы должны определить канал сообщений для получения сообщений, атрибут SERVICE_IN , который соответствуетКонфигурация targetService в JMS BC из листинга 7. С этим определением канала сообщений логика маршрутизации регистрируется как конечная точка службы JBI в контейнере ServiceMix. Затем мы определяем логику маршрутизации с выбором и когда и каким-либо другим способом. Когда входящее XML-сообщение является запросом на страхование автомобиля, оно направляется на конечные точки бюджета и страхования автомобилей класса люкс, а для запроса на страхование путешествия сообщение направляется на конвейер EIP.

Давайте сначала рассмотрим бюджетные конечные точки страхования автомобилей и элитных автомобилей в листинге 10. Обратите внимание, что этот список состоит из конфигурации JMS BC и конфигурации File BC. Конфигурация JMS BC фактически является частью того же файла xbean.xmlфайл, как показано в листинге 7, как вы можете видеть в исходном коде статьи.

Перечисление 10 Конфигурации JMS BC и File BC для конечных точек страхования автомобилей.

<!-- JMS BC SU configuration -->
<jms:provider service="esb:luxuryCarSender"
endpoint="jmsEndpoint"
destinationName="luxurycar.send"
connectionFactory="#connectionFactory"/>

<!-- File BC SU configuration -->
<beans xmlns:file="http://servicemix.apache.org/file/1.0"
xmlns:esb="http://esbinaction.com/insurance">

<classpath>
<location>.</location>
<location>bcel.jar</location>
<location>jibx-bind.jar</location>
<location>jibx-extras.jar</location>
<location>jibx-run.jar</location>
<location>qdox-1.6.1.jar</location>
<location>stax-api.jar</location>
<location>wstx-asl.jar</location>
<location>xmlpull_1_1_4.jar</location>
<location>xpp3.jar</location>
</classpath>

<file:sender service="esb:budgetCarSender"
endpoint="fileEndpoint"
directory="file:budgetCarIn"
marshaler="#InsuranceFileMarshaler"/>

<bean id="InsuranceFileMarshaler"
class="esb.dzone.servicemix.util.InsuranceFileMarshaler"/>
</beans>

Конфигурация JMS довольно стандартна, поскольку она просто отправляет сообщение в очередь JMS с именем luxury.send . Конфигурация File BC определяет отправителя файла, который отправляет сообщение от основанного на контенте маршрутизатора в каталог с именем budgetCarIn . Но, поскольку мы хотим, чтобы сообщение было отформатировано как файл CSV, мы определили еще один маршалер здесь. Файловый маршалер использует JiBX для преобразования XML-сообщения в Java-объект CarInsuranceRequest , который преобразуется в CSV-сообщение с логикой преобразования, аналогичной реализации примера Mule из предыдущей статьи. Давайте рассмотрим маршалер файла в листинге 11.

Листинг 11 Реализация маршалера файла.

public class InsuranceFileMarshaler extends DefaultFileMarshaler {

protected void writeMessageContent(MessageExchange exchange,
NormalizedMessage message, OutputStream out, String path)
throws MessagingException {
Source src = message.getContent();
if (src == null) {
throw new NoMessageContentAvailableException(exchange);
}
try {
CarInsuranceRequest request = (CarInsuranceRequest)
JiBXUtil.unmarshalDocument(src, CarInsuranceRequest.class);
String csvMessage = getInsuranceCSV(request);
OutputStreamWriter writer = new OutputStreamWriter(out);
writer.write(csvMessage);
writer.flush();
} catch (Exception e) {
throw new MessagingException(e);
}
}

private String getInsuranceCSV(CarInsuranceRequest request) {
return new StringBuffer()
.append(request.getRequestID())
.append(",")
.append(request.getNumberPlate())
.append(",")
.append(request.getCarType())
.append(",")
.append(request.getBuildYear())
.append(",")
.append(new SimpleDateFormat().format(request.getStartDate()))
.toString();
}
}

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

Прежде чем мы сможем отправить запрос на страхование путешествий в веб-службу, мы должны сначала преобразовать XML в целевой формат, определенный WSDL веб-службы. Чтобы немного сократить количество строк кода в этой статье (на мой взгляд, кода уже достаточно), мы сосредоточимся на конфигурации ServiceMix. Поскольку ServiceMix Saxon SE, который предоставляет функциональные возможности преобразования на основе XSLT, требует MEP для входа-выхода, нам сначала нужно преобразовать обмен сообщениями во вход-выход с помощью EIP-конвейера, как уже объяснено на рисунке 4. И поскольку CXF BC, которая обеспечивает функциональность веб-сервиса, также требует входного MEP, нам нужно определить два конвейера, как показано в листинге 12.

Листинг 12 Определения конвейера EIP, чтобы иметь возможность вызывать веб-сервис путешествия.

<beans xmlns:eip="http://servicemix.apache.org/eip/1.0"
xmlns:esb="http://esbinaction.com/insurance"
xmlns:tri="http://dzone.com/travelInsurance">

<eip:pipeline service="esb:travelPipeline" endpoint="routingEndpoint">
<eip:transformer>
<eip:exchange-target service="esb:transformTravelRequest"/>
</eip:transformer>
<eip:target>
<eip:exchange-target service="esb:travelServicePipeline" />
</eip:target>
</eip:pipeline>

<eip:pipeline service="esb:travelServicePipeline" endpoint="routingEndpoint">
<eip:transformer>
<eip:exchange-target service="tri:TravelInsuranceServiceImplService"
operation="tri:getTravelInsurance"/>
</eip:transformer>
<eip:target>
<eip:exchange-target service="esb:insuranceSender" />
</eip:target>
</eip:pipeline>
</beans>

Первое определение конвейера вызывается из реализации маршрутизатора на основе содержимого Camel, показанного в листинге 9. В первом конвейере мы вызываем Saxon SE, который преобразует сообщение XML в формат сообщения веб-службы. Затем первый конвейер вызывает второй конвейер для вызова веб-службы с входящим MEP. Итак, давайте быстро посмотрим на конфигурацию Saxon SE в листинге 13.

Перечисление 13 Конфигурация Saxon SE, которая преобразовывает сообщение XML в формат сообщения веб-службы путешествий.

<beans xmlns:saxon="http://servicemix.apache.org/saxon/1.0"
xmlns:esb="http://esbinaction.com/insurance">

<saxon:xslt service="esb:transformTravelRequest" endpoint="xsltEndpoint"
resource="classpath:TravelRequest.xslt" />
</beans>

Ну, это короткая конфигурация. Мы определяем файл XSLT для преобразования входящего XML-сообщения и определяем имена конечных точек службы Saxon JBI. Итак, давайте перейдем к конфигурации CXF BC в листинге 14, которая выполняет вызов веб-сервиса.

Перечисление 14 Конфигурация CXF BC, которая вызывает веб-сервис путешествия.

<beans xmlns:cxfbc="http://servicemix.apache.org/cxfbc/1.0"
xmlns:tri="http://dzone.com/travelInsurance">

<classpath>
<location>.</location>
</classpath>

<cxfbc:provider wsdl="classpath:travelinsurance.wsdl"
locationURI="http://localhost:9090/hello"
endpoint="TravelInsuranceServiceImplPort"
service="tri:TravelInsuranceServiceImplService"/>
</beans>

С файлом travelinsurance.wsdl, включенным в CXF BC SU, CXF BC может вызывать веб-службу путешествий в местоположении, указанном с помощью атрибута locationURI .

Хорошо, что завершает часть запроса. Мы покажем вам, как мы можем использовать основанный на контенте маршрутизатор, реализованный с помощью Apache Camel, для определения целевой конечной точки сообщения с запросом на страхование. Мы использовали список получателей для отправки запроса на страхование автомобиля на две конечные точки страхования, используя два разных транспорта, файл и JMS, в двух разных форматах, CSV и XML. Мы также показали, как веб-сервис страхования путешествий может быть вызван с помощью CXF BC, и как ответ этого веб-сервиса направляется обратно в очередь JMS, где прослушивает веб-сайт. Итак, мы уже сделали очень маленький раздел ответной части, так как мы уже перенаправили ответ от веб-сервиса обратно в очередь, которую слушает веб-сайт.

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

Реализация страхового брокера с ServiceMix: ответная часть

Как уже говорилось, мы уже определили большую часть конфигурации примера страхового брокера. Давайте сначала рассмотрим оставшиеся части ответной части как часть диаграммы SA на рисунке 9.

Рисунок 9 Обзор SA и SU для ответной части примера страхового брокера.

Мы уже определили CXF-провайдера и конвейерную конфигурацию вызова веб-службы Travel. Таким образом, единственная часть, которую мы должны обсудить для ответной части, это реализация агрегатора. Давайте рассмотрим конфигурацию агрегатора в листинге 15.

Листинг 15 Определение агрегатора, который объединяет два сообщения с ответами по страхованию автомобиля в одно сообщение о результате.

<beans xmlns:eip="http://servicemix.apache.org/eip/1.0"
xmlns:esb="http://esbinaction.com/insurance"
xmlns:tri="http://dzone.com/travelInsurance">

<eip:split-aggregator service="esb:insuranceAggregator" endpoint="routingEndpoint">
<eip:target>
<eip:exchange-target service="esb:insuranceSender" />
</eip:target>
</eip:split-aggregator>
</beans>

Это не так уж сложно, не так ли? Мы определяем имя конечной точки сервиса JBI агрегатора и целевой сервис, куда отправляется агрегированное сообщение. Чтобы иметь возможность использовать готовые функции агрегации агрегатора EIP, нам необходимо определить некоторые свойства сообщений, такие как идентификатор корреляции и количество сообщений, которые будут агрегированы. В JMS и файловых маршалерах, которые мы обсуждали в листинге 8 и 11, мы можем добавить эти свойства сообщения. В листинге 16 показана реализация файла маршалера.

Листинг 16. Обзор метода чтения файлового маршала, который добавляет свойства сообщения агрегации.

public class InsuranceFileMarshaler extends DefaultFileMarshaler {

public static final String FILE_NAME_PROPERTY = "org.apache.servicemix.file.name";
public static final String FILE_PATH_PROPERTY = "org.apache.servicemix.file.path";

public void readMessage(MessageExchange exchange, NormalizedMessage message,
InputStream in, String path) throws IOException, JBIException {

BufferedReader br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
String csvString = sb.toString();
String[] elements = csvString.split(",");
InsuranceResponse insuranceResponse = new InsuranceResponse();
insuranceResponse.setResponseID(elements[0]);
insuranceResponse.setRequestID(elements[1]);
insuranceResponse.setInsuranceCompanyName(elements[2]);
insuranceResponse.setPrice(Float.parseFloat(elements[3]));
Source source = null;
try {
source = JiBXUtil.marshalDocument(insuranceResponse, "UTF-8");
} catch(Exception e) {
throw new JBIException(e);
}
message.setContent(source);
message.setProperty(FILE_NAME_PROPERTY, new File(path).getName());
message.setProperty(FILE_PATH_PROPERTY, path);
message.setProperty("org.apache.servicemix.eip.splitter.corrid",
insuranceResponse.getRequestID());
message.setProperty("org.apache.servicemix.eip.splitter.index", 0);
message.setProperty("org.apache.servicemix.eip.splitter.count", new Integer(2));
}
}

Как показано в листинге 16, файловый опросер сначала преобразовывает сообщение CSV в Java-объект InsuranceResponse . Затем объект InsuranceResponse преобразуется в сообщение XML с помощью JiBX. Прежде чем отправлять сообщение XML в контейнере ServiceMix, мы добавляем свойства сообщения агрегатора EIP. Первое свойство — это идентификатор корреляции, затем индексный номер сообщения и количество сообщений, подлежащих агрегированию. В маршалере JMS мы реализовали аналогичную функциональность, но свойство сообщения индекса установлено в 1.

И это все для ответной части!

Протестируйте страхового брокера с ServiceMix

Чтобы по-настоящему понять конфигурации ServiceMix, показанные в примере страхового брокера, вы можете использовать исходный код для запуска примера. Мы предоставили скрипт сборки Ant, build.xml , чтобы вам было проще. С целью запуска вы можете запустить контейнер ServiceMix, а с целью развертывания страхования вы можете развернуть полную сборку службы страхового брокера в контейнере ServiceMix.

Затем вы можете использовать тест InsuranceTest JUnit, чтобы отправить сообщение JMS для запуска потока сообщений страхового брокера. С помощью теста LuxuryCarTest JUnit вы можете отправить ответное сообщение о роскошном автомобиле и получить ответ.Файл доступен в каталоге файлов SU для отправки ответа в бюджет автомобильной страховой компании. InsuranceTest также включает в себя код для имитации путешествия веб — службы.

Вывод

В этой статье мы показали, как вы можете реализовать пример интеграции с ServiceMix. Как вы можете видеть, между примерами реализации страхового брокера Mule и ServiceMix есть немало различий, но конфигурация также имеет некоторые сходства. Как Mule, так и ServiceMix широко используют Spring для настройки логики интеграции, а оба ESB используют стиль конфигурации на основе XML.

Основные различия связаны с основанием JBI ServiceMix и специфической архитектурой Mule Mule. JBI использует SU и SA, а Mule определяет все как часть файла mule-config.xml. Другое отличие заключается в использовании сообщений XML или нормализованных сообщений в ServiceMix (согласно спецификации JBI) и объектов Java в Mule. Mule принимает объекты Java как полезную нагрузку сообщения, но ServiceMix требует использования полезной нагрузки XML. Третье отличие — это функция горячего развертывания ServiceMix, которой нет в реализации Mule. С ServiceMix вы можете легко развертывать новые версии SA, пока ServiceMix продолжает работать. С Mule вам придется перезапустить контейнер, прежде чем можно будет загрузить новую конфигурацию Mule.

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

Дополнительные ресурсы

1. ServiceMix — http://servicemix.apache.org
2. ServiceMix 4 — http://servicemix.apache.org/SMX4/index.html
3. Шаблоны корпоративной интеграции — http://www.enterpriseintegrationpatterns.com
4. Apache Camel — http://activemq.apache.org/camel
5. Загрузить исходный код статьи — http://www.esbinaction.com/files/dzoneservicemix.zip

Йос Дирксен

Йос — архитектор программного обеспечения, работающий в Atos Origin и специализирующийся на интеграции предприятий. Джос является соавтором будущей книги Мэннинга «ESB с открытым исходным кодом в действии» ( http://www.manning.com/rademakers ). Он часто выступает на конференциях Java, таких как JavaPolis, JavaZone, JavaOne и NL-JUG, о проектах корпоративной интеграции с открытым исходным кодом, таких как Mule, ServiceMix, jBPM и Axis2.

Тийс Радемакерс

Tijs — разработчик программного обеспечения, работающий на Atos Origin и специализирующийся на интеграции предприятия. Tijs является соавтором будущей книги Мэннинга «ESB с открытым исходным кодом в действии» ( http://www.manning.com/rademakers ). Он часто выступает на конференциях по Java, таких как JavaPolis, JavaZone, JavaOne и NL-JUG, о проектах корпоративной интеграции с открытым исходным кодом, таких как Mule, ServiceMix, Apache Synapse и Apache Tuscany.