четверг, 7 июля 2011 г.

Построение композитного сервиса с помощью Oracle Service Bus


В состав Oracle Service Bus входит очень мощное средство управления последовательностью вызовов сервисов, которое называется Split-Join. Данный компонент позволяет разделить входное сообщение на части (паттерн Splitter, здесь и далее будут упоминаться паттерны из каталога EAI), пропустить данные части через разные маршруты обработки, а затем агрегировать результаты (паттерн Agregator). Сервис, агрегирующий результаты вызовов других сервисов называется композитным.

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

Создание сервисов UserInfo и UserEmails


Сервисы UserInfo и UserEmails мы реализуем с помощью Oracle SOA Suite. Данные сервисы будут представлять собой синхронные BPEL-процессы, принимающие на вход идентификатор пользователя и возвращающие структуры данных с информацией о пользователе и списком почтовых адресов, соответственно.

Создание каждого сервиса начнем с определения контракта - типов данных, используемых для представления запроса и ответа. Опишем контракт сервиса UserInfo в виде схемы UserInfo.xsd, следующего содержания:

  1. <?xml version="1.0" encoding="UTF-8" ?>

  2. <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

  3.            xmlns:rtk="http://www.tsretail.ru/integration/user"

  4.            targetNamespace="http://www.tsretail.ru/integration/user"

  5.            elementFormDefault="qualified">

  6.  

  7.     <xsd:element name="user" type="rtk:tUser"/>

  8.    

  9.     <xsd:element name="userId" type="rtk:tUserId"/>

  10.  

  11.     <xsd:simpleType name="tUserId" >

  12.         <xsd:restriction base="xsd:string">

  13.             <xsd:length value="4"/>

  14.         </xsd:restriction>

  15.     </xsd:simpleType>

  16.  

  17.     <xsd:complexType name="tUser">

  18.         <xsd:sequence>

  19.             <xsd:element name="name" type="xsd:string"/>

  20.             <xsd:element name="sername" type="xsd:string"/>

  21.             <xsd:element name="login" type="xsd:string"/>

  22.         </xsd:sequence>

  23.     </xsd:complexType>

  24. </xsd:schema>

  25.  



Контракт сервиса UserEmails будет представлен в виде схемы UserEmails.xsd:

  1. <?xml version="1.0" encoding="UTF-8" ?>

  2. <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

  3.            xmlns:rtk="http://www.tsretail.ru/integration/emails"

  4.            targetNamespace="http://www.tsretail.ru/integration/emails"

  5.            elementFormDefault="qualified">

  6.            

  7.     <xsd:element name="emails" type="rtk:tUserEmails"/>

  8.    

  9.     <xsd:element name="userId" type="rtk:tUserId"/>

  10.  

  11.     <xsd:simpleType name="tUserId" >

  12.         <xsd:restriction base="xsd:string">

  13.             <xsd:length value="4"/>

  14.         </xsd:restriction>

  15.     </xsd:simpleType>

  16.            

  17.     <xsd:complexType name="tUserEmails">

  18.         <xsd:sequence>

  19.             <xsd:element name="email" type="xsd:string"

  20.                         maxOccurs="unbounded" minOccurs="0" />

  21.         </xsd:sequence>

  22.     </xsd:complexType>

  23. </xsd:schema>

  24.  



Бизнес-логика сервисов довольно проста: в BPEL-процессе происходит присваивание констант исходящим переменным.

UserInfo:




UserEmails:





После разворачивания на сервере SOA Suite сервисы можно протестировать. Для этого необходимо перейти на вкладку соответствующего композита в Enterprise Manager и нажать кнопку Test. В появившемся окне необходимо заполнить значение параметра payload



...и нажать кнопку Test Web Service. Появится окно с результатами вызова сервиса.



Аналогично для сервиса UserEmails.





По результатам тестирования можно сделать вывод: сервисы готовы к работе.


Настройка компонента Split-Join


Для демонстрации использования созданных сервисов в Oracle Service Bus (OSB) необходимо создать новый OSB-проект в Oracle Enterprise Pack for Eclipse. Подробно создание проекта и настройка Business Services и Proxy Services рассмотрена в статье Создаем Hello World на Oracle Service Bus с использованием Oracle Enteprise Pack for Eclipse. Воспользовавшись инструкциями, приведенными в данной заметке, создадим следующую структуру проекта:



Теперь необходимо создать WSDL-описание для композитного сервиса. Создать WSDL-описание можно с помощью пункта File -> New -> WSDL основного меню Eclipse.



После указания наименования файла и выбора его расположения появится окно настройки WSDL-описания. В появившемся окне необходимо указать целевое пространство имен, которое будет использовано в WSDL, префикс и отметить галочку Create WSDL Skeleton, после чего можно нажать кнопку Finish.



Необходимо заполнить WSDL-файл следующим текстом:

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>

  2. <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.tsretail.ru/integration/core" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="UserService" targetNamespace="http://www.tsretail.ru/integration/core">

  3.   <wsdl:types>

  4.     <xsd:schema targetNamespace="http://www.tsretail.ru/integration/core">

  5.       <xsd:element name="getUser">

  6.         <xsd:complexType>

  7.           <xsd:sequence>

  8.             <xsd:element name="userId" type="xsd:string"/>

  9.           </xsd:sequence>

  10.         </xsd:complexType>

  11.       </xsd:element>

  12.       <xsd:element name="getUserResponse">

  13.         <xsd:complexType>

  14.           <xsd:sequence>

  15.             <xsd:element name="user">

  16.                 <xsd:complexType>

  17.                     <xsd:sequence>

  18.                         <xsd:element name="name" type="xsd:string"/>

  19.                         <xsd:element name="family" type="xsd:string"/>

  20.                         <xsd:element name="login" type="xsd:string"/>

  21.                         <xsd:element name="emails">

  22.                             <xsd:complexType>

  23.                                 <xsd:sequence>

  24.                                     <xsd:element name="email" type="xsd:string"

  25.                                                  maxOccurs="unbounded" minOccurs="0"/>

  26.                                 </xsd:sequence>

  27.                             </xsd:complexType>

  28.                         </xsd:element>

  29.                     </xsd:sequence>

  30.                 </xsd:complexType>

  31.             </xsd:element>

  32.           </xsd:sequence>

  33.         </xsd:complexType>

  34.       </xsd:element>

  35.     </xsd:schema>

  36.   </wsdl:types>

  37.   <wsdl:message name="getUserRequest">

  38.     <wsdl:part element="tns:getUser" name="parameters"/>

  39.   </wsdl:message>

  40.   <wsdl:message name="getUserResponse">

  41.     <wsdl:part element="tns:getUserResponse" name="parameters"/>

  42.   </wsdl:message>

  43.   <wsdl:portType name="UserService">

  44.     <wsdl:operation name="getUser">

  45.       <wsdl:input message="tns:getUserRequest"/>

  46.       <wsdl:output message="tns:getUserResponse"/>

  47.     </wsdl:operation>

  48.   </wsdl:portType>

  49.   <wsdl:binding name="UserServicePortBinding" type="tns:UserService">

  50.     <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

  51.     <wsdl:operation name="getUser">

  52.       <soap:operation soapAction=""/>

  53.       <wsdl:input>

  54.         <soap:body use="literal"/>

  55.       </wsdl:input>

  56.       <wsdl:output>

  57.         <soap:body use="literal"/>

  58.       </wsdl:output>

  59.     </wsdl:operation>

  60.   </wsdl:binding>

  61.   <wsdl:service name="UserService">

  62.     <wsdl:port binding="tns:UserServicePortBinding" name="UserServicePort">

  63.       <soap:address location="http://www.tsretail.ru/integration/UserService"/>

  64.     </wsdl:port>

  65.   </wsdl:service>

  66. </wsdl:definitions>

  67.  



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

Теперь мы готовы создать компонент Split-Join. Компонент Split-Join можно создать, воспользовавшись пунктом New -> Split-Join контекстного меню вида Project Explorer.



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



На втором шаге мастера необходимо выбрать WSDL-файл и операцию, которая будет реализована с помощью компонента Split-Join.



По-умолчанию структура компонента Split-Join следующая:



Он содержит заголовок - список глобальных переменных и ссылок на сервисы, операцию Receive и операцию Reply.

Разместим блок Parallel между операциями Receive и Reply, перетащив его с палитры компонентов.



По-умолчанию в блоке Parallel две ветви. Нам нужно обеспечить вызовы двух сервисов, поэтому добавлять новые ветви не нужно, однако при необходимости это можно сделать.

В каждую ветвь поместим по операции Invoke Service. Назовем их Invoke UserInfo и Invoke UserEmails, соответственно. Как видно из названия, данные операции обеспечивают непосредственный вызов сервисов.



Однако, чтобы данные операции корректно работали, их необходимо настроить. Настройка осуществляется с помощью вкладок, расположенных в виде Properties. Прежде всего необходимо выбрать операцию - значение параметра Opertaion вкладки Operation. Выбор осуществляется с помощью кнопки Browse и диалогового окна Service Browser.



Параметр Service Location задает путь к сервису, в нашем случае это - путь к Business Service UserInfo.

Параметр QoS - Quality of Service задает контекст транзакции, в которой будет осуществляться вызов сервиса. Значение Best Effort обозначает, что сервис будет вызываться вне контекста транзакции, значение Exactly Once обозначает, что сервис будет вызываться в контексте текущей транзакции.



Теперь необходимо настроить входящую и исходящую переменные, которые будут использоваться для вызова сервиса. Входящая переменная настраивается на вкладке Input Variable. Прежде всего ее нужно создать, выбрав в качестве значения параметра Message Variable - Create Message Variable.



В появившемся окне необходимо указать название переменной. Тип и пространство имен будут подставлены автоматически на основании описания выбранного сервиса. Переменная может быть как локальной - ее область видимости будет ограничена блоком Parallel, так и глобальной - ее областью видимости будет весь компонент Split-Join.



Переменная userId нужна только для операции Invoke Service, поэтому ее разумно сделать локальной. Созданная переменная будет подставлена в качестве значения параметра Message Variable.



Исходящая переменная создается аналогично, только на вкладке Output Variable. Данную переменную необходимо сделать глобальной, т.к. ее значение будет использоваться при формировании ответа сервиса после выполнения блока Parallel.



Созданная переменная будет подставлена в качестве значения параметра Message Variable.



Глобальная переменная userInfo отобразится в списке Variables в заголовке компонента Split-Join.



Настройка вызова сервиса UserEmails осуществляется аналогично.

Входящей переменной перед вызовом сервиса необходимо присвоить значение. Для того, чтобы присвоить значение переменной необходимо использовать операцию Assign, разместив ее перед операцией Invoke Service.



Операция Assign настраивается на вкладке Assign вида Properties.
Назначения параметров следующие:

  • Expression - выражение, результат которого будет присваиваться;

  • Variable - переменная, в которую будет осуществляться присваивание;

  • QoS - Quality of Service, будет ли присваивание осуществляться в контексте текущей транзакции или нет.





Для редактирования параметра Expression используется стандартный редактор выражений, работа с которым описана в заметке Динамическая маршрутизация в Oracle Service Bus.



Нам нужно присвоить переменной userId значение userId из запроса. Это довольно простая операция, поэтому здесь нет смысла использовать XQuery- или XSLT-преобразования. Воспользуемся вкладкой Expression и просто перетащим нужную нам часть переменной-запроса в поле редактирования выражения.

В качестве значения параметра Variable выберем из списка доступных переменных userId.payload.



Для действия Assign в ветке вызова сервиса UserEmails необходимо задать аналогичные настройки.

После проведения описаных выше манипуляций сервисы UserInfo и UserEmails будут корректно вызываться. Однако нам нужно еще и объединить результат их вызовов в ответ композитного сервиса, создаваемого с помощью компонента Split-Join. Для этого перед действием Reply, за блоком Parallel, следует разместить операцию Assign.



Прежде чем настраивать действие Assign, необходимо создать файл XQuery-преобразований, с помощью которого мы будем конвертировать ответы сервисов UserInfo и UserEmails в ответ создаваемого композитного сервиса. Создание и настройка файла XQuery-преобразований описана в статье Трансформация сообщений в Oracle Service Bus с использованием XSLT и XQuery.

Прежде всего необходимо создать файл AgregationTransformation.xq.



В качестве типов-источников требуется выбрать типы элементов emails и user - типы ответов сервисов получения информации о почтовых адресах и о пользовательской информации.



В качестве типа-цели необходимо выбрать тип getUserResponse - тип ответа сервиса UserService.



Сама трансформация предствалена на рисунке:



Созданный файл трансформации необходимо выбрать в качестве значения параметра XQuery вкладки XQuery Resources окна редактирования поля Expression действия Assign Response. После чего необходимо настроить связывание параметра user с переменной userInfo.payload, а параметра emails с переменной userEmails.payload.



В качестве значения параметра Variable действия Assign Response необходимо выбрать переменную response.parameters.



Компонент Split-Join создан и настроен.

Создание Business Service и Proxy Service


В Oracle Service Bus композитный сервис, созданный с помощью компонента Split-Join рассматривается как Service Provider. Т.е., чтобы использовать данный сервис к нему нужно подключиться так же, как и к сервису, расположенному вне OSB - через соответствующий Business Service. Проще всего создать такой Business Service, воспользовавшись пунктом Oracle Service Bus -> Generate Business Service контекстного меню компонента Split-Join.



В диалоговом окне генерации Business Service необходимо выбрать его расположение и указать название файла, который будет содержать описание сервиса.



Будет сгенерирован Business Service, имеющий тип Transport Typed Service



...и использующий flow в качестве транспортного протокола.



Чтобы созданный Business Service можно было протестировать и в дальнейшем использовать, необходимо создать соответствующий Proxy Service. Proxy Service должен быть основан на WSDL-файле UserService.wsdl



а в потоке обработки сообщений должна быть настроена маршрутизация на Business Service, сгенерированный для компонента Split-Join - на UserServiceSplitJoin.



Тестирование композитного сервиса


После того, как проект Agregator будет развернут на сервере OSB, его можно будет протестировать с помощью консоли управления. Для тестирования необходимо открыть вкладку Proxy Services, в таблице Resources которой будет присутствовать UserProxyService.



Чтобы отправить тестовый запрос, нужно нажать кнопку Launch Test Console. В появившемся диалоговом окне необходимо задать значения тестовых параметров - пятисимвольную строку, обозначающую идентификатор пользователя, и нажать кнопку Execute.



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



Ресурсы


Статья написана по мотивам Oracle Service Bus, Implementing Aggregator pattern by use of Split-Join. Так же про использование компонента Split-Join для ускорения обработки запросов можно прочитать в официальной документации Oracle и в блоге Edwin Biemond'а.

Критика, замечания, комментарии и вопросы как всегда приветствуются.

Понравилось сообщение - подпишитесь на блог

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

Отправить комментарий

Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!