среда, 9 мая 2012 г.

Асинхронные веб-сервисы

В данной статье описаны подходы к реализации асинхронного взаимодействия между информационными системами с помощью веб-сервисов. Подробно рассмотрен применяющийся в Oracle SOA Suite подход, основанный на использовании сервиса обратного вызова и стандарта WS-Addressing. Приведены примеры создания асинхронного веб-сервиса с помощью Oracle SOA Suite и генерации клиента к такому сервису с помощью интегрированной среды разработки Oracle JDeveloper.

Введение


Зачастую при интеграции информационных систем необходимо обеспечить асинхронное взаимодействие между ними. При данном типе взаимодействия потребитель сервиса не ждет окончания обработки запроса поставщиком сервиса, а сразу после получения уведомления о принятии данного запроса к обработке продолжает свою работу. Существует два подхода к обеспечению асинхронного взаимодействия с помощью механизма веб-сервисов:
  • полинг - повторяющийся опрос. Потребитель сервиса периодически запрашивает у поставщика статус и результат обработки запроса;
  • обратный вызов. Поставщик сервиса после завершения обработки запроса уведомляет потребителя, вызывая специальный метод.

В Oracle SOA Suite применяется второй подход: потребитель сервиса является одновременно поставщиком сервиса обратного вызова. В свою очередь поставщик сервиса после получения запроса от потребителя возвращает ему HTTP-ответ 202 Accepted, что обозначает успешное принятие запроса на обработку. В Oracle SOA Suite при этом создается новый экземпляр композита. После завершения обработки запроса, которая может занимать достаточно много времени, например недели, что особенно характерно, если в обработке запроса участвуют люди, поставщик сервиса осуществляет вызов сервиса обратного вызова, предоставляемого потребителем.

WS-Addressing


При реализации механизма обратного вызова возникает необходимость ответить на два вопроса: как передать поставщику сервиса адрес, по которому доступен сервис обратного вызова, и как связать сообщение-запрос с сообщением-ответом, т.е. как дать поставщику сервиса понять, ответом на какой конкретный запрос является сообщение, полученное сервисом обратного вызова. Для решения данных проблем разработана спецификация Web Services Addressing (WS-Addressing). Данная спецификация позволяет включать в заголовки SOAP-сообщений информацию о маршрутизации. С одной стороны она позволяет описывать адреса конечных точек, являющихся поставщиками или потребителями сообщений, а с другой стороны - позволяет связывать конкретное сообщение с конечной точкой. При этом важно понимать, что заголовок SOAP-сообщения является частью протокола прикладного уровня, таким образом WS-Addressing - это способ отвязать информацию о маршруте сообщения от транспортного уровня модели OSI и передать ее на прикладной уровень. Именно данное свойство спецификации обеспечивает возможность разделить время жизни SOAP запроса/ответа и время жизни HTTP запроса/ответа, что и позволяет реализовать асинхронные веб-сервисы.

Давайте рассмотрим асинхронный обмен с использованием WS-Addressing на примере. Потребитель сервиса формирует следующий запрос:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"

             xmlns:wsa="http://www.w3.org/2005/08/addressing"

             xmlns:ins="http://xmlns.oracle.com/sca/tracking/1.0">

    <env:Header>

        <wsa:To>http://hostname:8001/soa-infra/services/default/AysncB/client_ep</wsa:To>

        <wsa:Action>http://xmlns.oracle.com/AsyncA/AsyncB/BPELProcessB/

                    BPELProcessB/process</wsa:Action>

        <wsa:MessageID>urn:62772860C2DE8F6A0634D09B</wsa:MessageID>

        <wsa:RelatesTo>urn:62772860C2DE8F6A0634D09B</wsa:RelatesTo>

        <wsa:ReplyTo>

            <wsa:Address>

                http://hostname:8001/soa-infra/services/default/AsyncA!1.0*

                2fc449a6-fa51-440a-afae-b143d9c26d88/BPELProcessB%23BPEL

                ProcessA/BPELProcessB

            </wsa:Address>

            <wsa:ReferenceParameters>

                <ins:tracking.ecid>...</ins:tracking.ecid>

                <ins:tracking.conversationId>...</ins:tracking.conversationId>

                <ins:tracking.parentComponentInstanceId>...</ins:tracking.parentComponentInstanceId>

            </wsa:ReferenceParameters>

        </wsa:ReplyTo>

    </env:Header>

    <env:Body>

    :

    </env:Body>

</env:Envelope>

 

В данном сообщении поле заголовка wsa:To служит для указания адресата запроса, т.е. конечной точки, на которую отправляется запрос. Поле wsa:Action используется для указания операции, которую необходимо выполнить. Уникальный идентификатор сообщения передается в поле wsa:MessageID. Уникальный идентификатор связанного сообщения, т.е. того сообщения, на которое осуществляется ответ, передается в поле wsa:RelatesTo. Т.к. мы сейчас рассматриваем не ответ, а запрос, то у нас идентификаторы сообщений в полях wsa:MessageID и wsa:RelatesTo совпадают.

В поле wsa:ReplyTo/wsa:Address передается адрес конечной точки сервиса обратного вызова. Именно по этому адресу будет передан ответ на данный запрос. Другие поля, определенные внутри элемента wsa:ReplyTo, служат для передачи дополнительных параметров связанных сообщений.

В ответ на рассмотренный нами запрос, поставщик сервиса формирует следующее сообщение:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"

             xmlns:wsa="http://www.w3.org/2005/08/addressing"

             xmlns:ins="http://xmlns.oracle.com/sca/tracking/1.0">

    <env:Header>

        <wsa:To>http://hostname:8001/soa-infra/services/default/AsyncA!1.0*

                2fc449a6-fa51-440a-afae-b143d9c26d88/BPELProcessB%23BPEL

                ProcessA/BPELProcessB

        </wsa:To>

        <wsa:Action>processResponse</wsa:Action>

        <wsa:MessageID>urn:62772860C2EBFB26F9ED8D4E</wsa:MessageID>

        <wsa:RelatesTo>urn:62772860C2DE8F6A0634D09B</wsa:RelatesTo>

        <wsa:ReplyTo>

            <wsa:Address>

                http://www.w3.org/2005/08/addressing/anonymous

            </wsa:Address>

            <wsa:ReferenceParameters>

                <ins:tracking.conversationId>...</ins:tracking.conversationId>

                <ins:tracking.parentComponentInstanceId>...</ins:tracking.parentComponentInstanceId>

            </wsa:ReferenceParameters>

        </wsa:ReplyTo>

    </env:Header>

    <env:Body>

    :

    </env:Body>

</env:Envelope>

 

Видно, что в данном сообщении значение поля wsa:To соответствует значению поля wsa:ReplyTo/wsa:Address запроса. В свою очередь значение поля wsa:RelatesTo соответствует значению поля wsa:MessageID запроса. Т.к. рассматриваемое нами сообщение является ответом, т.е. на него в свою очередь ответа ожидать не стоит, то в качестве значения поля wsa:ReplyTo/wsa:Address используется заглушка - анонимный адрес http://www.w3.org/2005/08/addressing/anonymous.

Создание асинхронного веб-сервиса с помощью Oracle SOA Suite


Рассмотрим как с помощью Oracle SOA Suite можно создать асинхронный веб-сервис. Для реализации бизнес-логики данного сервиса будем использовать асинхронный BPEL-процесс.

Прежде всего следует создать приложение. Для этого в Oracle JDeveloper необходимо запустить соответствующий мастер, выполнив команду New -> Applications -> SOA Application.


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


На втором шаге мастера создается проект. Здесь необходимо указать наименование и каталог проекта, а так же выбрать используемые технологии. Так как мы создаем SOA-приложение, то технология SOA для проекта уже будет выбрана.


На третьем шаге мастера необходимо указать наименование и используемый шаблон создаваемого композита Oracle SOA Suite. Так как сначала нам нужно создать XSD-схемы с описанием интерфейсов разрабатываемого сервиса, то создадим пустой композит.


После нажатия кнопки Finish композит будет создан. Для реализации логики композита нам нужно поместить в него BPEL-процесс, однако сначала необходимо описать интерфейсы создаваемого веб-сериса. Прежде всего нужно описать структуру сообщений. Для этого необходимо создать XSD-схему, воспользовавшись соответствующим мастером.


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


Сначала опишем необходимые бизнес-сущности, которыми будет манипулировать веб-сервис. Для примера мы создадим веб-сервис, который будет осуществлять интеграцию с системой класса HelpDesk. С его помощью мы будем создавать в данной системе заявки и при необходимости добавлять к ним комментарии. Таким образом нам необходимо создать два типа данных: tTicket - комплексный тип, описывающий заявку и tTicketComment - комплексный тип, описывающий комментарий к заявке. Структура данных типов приведена на рисунке:


После создания бизнес-сущностей необходимо разработать сообщения, с помощью которых данные бизнес-сущности будут передаваться в веб-сервис. Для сообщений создадим отдельный XSD-файл.


Данный файл будет содержать описание четырех типов данных и, соответственно, четырех элементов данных типов: запрос на создание заявки, запрос на добавление комментария и ответы на данные запросы. Структура файла TicketService.xsd таким образом будет следующая:


После создания описания сообщений, можно перейти к разработке веб-сервиса. Начнем с реализации логики операции CreateTicket, которая будет инкапсулирована в BPEL-процессе CreateTicketBPELProcess. Для создания процесса необходимо переместить с палитры компонентов в поле композита сервисный компонент BPEL.


После этого откроется мастер создания BPEL-процесса. В окне мастера необходимо выбрать версию спецификации WS-BPEL: 1.1 или 2.0, задать наименование процесса и используемое в нем целевое пространство имен, определить тип процесса: синхронный, асинхронный или однонаправленный (т.е. не возвращающий ответа), а также указать формат входного и, при необходимости, выходного сообщения.


После нажатия кнопки OK созданный BPEL-процесс будет помещен в поле композита. Так как при создании данного процесса была отмечена галочка Expose as a SOAP service, то вместе с BPEL-процессом будет создан компонент WebService, являющийся точкой входа в композит.


Предполагается, что после получения запроса от потребителя разрабатываемый нами веб-сервис помещает его в JMS-очередь. Система-приемник считывает данный запрос из очереди, обрабатывает его и формирует ответ, который помещает в JMS-очередь ответов. BPEL-процесс считывает ответ из данной очереди и передает его на сервис обратного вызова, выставленный потребителем.

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


На третьем шаге мастера необходимо выбрать тип используемой JMS-подсистемы, например Oracle WebLogic JMS.


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


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


На шестом шаге мастера необходимо указать паттерн взаимодействия с JMS. Адаптер может слушать очередь с целью получить сообщение, может помещать сообщения в очередь или может реализовывать двунаправленное взаимодействие по принципу запрос/ответ, причем как синхронное, так и асинхронное.


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


На восьмом шаге мастера необходимо выбрать очередь для ответов и аналогично предыдущему шагу задать параметры сообщения.


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


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


После нажатия кнопки Finish в поле композита отобразится созданный JMS-адаптер. Его необходимо соединить с BPEL-процессом.


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


Интересным является последнее действие в BPEL перед отправкой обратного вызова. Так как Oracle SOA Suite является средой с сохранением состояния, то при отправке асинхронного ответа можно использовать параметры, которые были подготовлены ранее. Состояние обработки запроса сохраняется в базе данных, поэтому даже если между получением запроса и отправкой ответа сервер SOA Suite был перезагружен, данное состояние не теряется.


После создания композитного приложения его необходимо развернуть на сервере. Для этого необходимо вызвать пункт Deploy... контекстного меню композита.


На первом шаге появившегося мастера разворачивания приложения необходимо выбрать цель разворачивания: сервер приложений или файл.


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


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


На четвертом шаге мастера необходимо выбрать раздел на сервере приложений. Раздел - группа композитов, объединенных по какому-либо признаку.


Последний шаг мастера является информационным: на нем отображаются сведения о том, как именно композит будет развернут на сервере приложений.


После нажатия кнопки Finish запустится процесс разворачивания приложения. После его успешного завершения в журнале появится надпись Deployment Finished.


После разворачивания веб-сервиса можно посмотреть его WSDL-описание. Так как веб-сервис создан на основе BPEL-процесса, то его WSDL-описание содержит описание типов партнерских связей, соединяющих между собой асинхронные запрос и ответ.

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

<wsdl:definitions

    name="CreateTicketBPELProcess"

    targetNamespace="http://xmlns.oracle.com/TicketApplication/TicketService/CreateTicketBPELProcess"

    xmlns:ns1="http://www.url.ru/integration/service/ticket"

    xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"

    xmlns:client="http://xmlns.oracle.com/TicketApplication/TicketService/CreateTicketBPELProcess"

    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

   >

    <wsdl:documentation>

        <abstractWSDL>http://SAMOLISOV:7001/soa-infra/services/default/TicketService!1.0/CreateTicketBPELProcess.wsdl</abstractWSDL>

    </wsdl:documentation>

    <plnk:partnerLinkType name="CreateTicketBPELProcess">

        <plnk:role name="CreateTicketBPELProcessProvider">

            <plnk:portType name="client:CreateTicketBPELProcess"/>

        </plnk:role>

        <plnk:role name="CreateTicketBPELProcessRequester">

            <plnk:portType name="client:CreateTicketBPELProcessCallback"/>

        </plnk:role>

    </plnk:partnerLinkType>

<wsp:Policy xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="wsaddr_policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">

   <wsaw:UsingAddressing xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"/>

</wsp:Policy>

Важно! Подобные партнерские связи обязательно должны присутствовать в описании веб-сервиса, иначе к нему нельзя будет сгенерировать клиент (точнее сам клиент можно, а вот веб-сервис обратного вызова - нет), по крайней мере с помощью JDeveloper.

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

    <wsdl:portType name="CreateTicketBPELProcess">

        <wsdl:operation name="process">

            <wsdl:input message="client:CreateTicketBPELProcessRequestMessage"/>

        </wsdl:operation>

    </wsdl:portType>

    <wsdl:portType name="CreateTicketBPELProcessCallback">

        <wsdl:operation name="processResponse">

            <wsdl:input message="client:CreateTicketBPELProcessResponseMessage"/>

        </wsdl:operation>

    </wsdl:portType>

    <wsdl:binding name="CreateTicketBPELProcessCallbackBinding" type="client:CreateTicketBPELProcessCallback">

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

        <wsdl:operation name="processResponse">

            <soap:operation style="document" soapAction="processResponse"/>

            <wsdl:input>

                <soap:body use="literal" namespace="http://xmlns.oracle.com/TicketApplication/TicketService/CreateTicketBPELProcess"/>

            </wsdl:input>

        </wsdl:operation>

    </wsdl:binding>

    <wsdl:binding name="CreateTicketBPELProcessBinding" type="client:CreateTicketBPELProcess">

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

      <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" URI="#wsaddr_policy" wsdl:required="false"/>

        <wsdl:operation name="process">

            <soap:operation style="document" soapAction="process"/>

            <wsdl:input>

                <soap:body use="literal"/>

            </wsdl:input>

        </wsdl:operation>

    </wsdl:binding>

    <wsdl:service name="TicketService_ep">

        <wsdl:port name="CreateTicketBPELProcess_pt" binding="client:CreateTicketBPELProcessBinding">

            <soap:address location="http://samolisov:7001/soa-infra/services/default/TicketService/TicketService_ep"/>

        </wsdl:port>

    </wsdl:service>

 

Протестируем приложение. Для этого в Oracle Enterprise Manager откроем страницу композита и нажмем кнопку Test. Появится форма, на которой можно задать значения входных параметров веб-сервиса.


После нажатия на кнопку Test Web Service сформированный тестовый запрос будет отправлен на вход веб-сервиса. Однако, т.к. наш веб-сервис является асинхронным, то вместо странички с ответом, отобразится страница с информацией об успешном осуществлении вызова.


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


Давайте посмотрим содержимое очереди запросов - jms/request. В данной очереди находится одно сообщение, сформированное при вызове веб-сервиса.


Попробуем сэмулировать ответ от внешней системы. Для этого руками создадим соответствующее сообщение в очереди jms/response. Важно! Значение поля Correlation ID данного сообщения должно совпадать со значением поля Message ID сообщения-запроса.


Запущенный экземпляр композита считает новое сообщение из очереди и завершит свою работу. Однако, т.к. при вызове сервиса поля заголовка WS-Addressing не заполнялись, то и сообщение на сервис обратного вызова отправлено не будет.


Создание клиента к асинхронному веб-сервису с помощью Oracle JDeveloper


Интегрированная среда разработки от корпорации Oracle - JDeveloper - содержит в себе все необходимые средства для генерации клиентов к веб-сервисам, в том числе и асинхронным. Для демонстрации работы с данными средствами продолжим пример, начатый в предыдущем разделе, - разработаем клиента к созданному веб-сервису. Для этого нам необходимо создать отдельный, так называемый Generic проект.


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


Созданный проект появится в дереве навигации JDeveloper'а.


Теперь необходимо сгенерировать заглушки (стабы) для клиента веб-сервиса. В терминах JDeveloper'а данные заглушки называются Web Service Proxy и меню для их генерации находится в листе Business Tier/Web Services дерева категорий (Categories).


После запуска мастера создания Web Service Proxy нам предложат выбрать используемую спецификацию: JAX-RPC или JAX-WS. Остановимся на последней.


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


Затем необходимо указать имена пакетов для сгенерированных заглушек, а так же для сгенерированных типов и обратного вызова. Так как WSDL-описание сервиса включает в себя тип партерской связи между типом порта запроса и типом порта ответа, то галочка Generate As Async будет доступна. Если ее отметить, то помимо заглушек клиента веб-сервиса будет сгенерирован веб-сервис обратного вызова.


На странице Port Endpoints необходимо задать адрес конечной точки веб-сервиса.


На странице Asynchronous Methods необходимо выбрать для каких методов генерировать асинхронные заглушки. Возможные варианты: не генерировать асинхронные заглушки, генерировать только для методов, специфицированных с помощью JAX-WS связывания или для всех методов.



На странице Policy необходимо выбрать используемые с помощью Oracle Web Service Manager (OWSM) политики. В WSDL-описании веб-сервиса указана только одна политика: WS-Addressing, которой соответствует значение oracle/wsaddr_policy поля Addressing.


На странице Defined Handlers при необходимости можно указать дополнительные обработчики сообщений.


Последняя страница мастера - Finish - является информационной: на ней отображаются сведения о том, какие методы будут сгенерированы.


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


Код обработчика обратного вызова следующий:


После комментария Add your implementation here. можно поместить свой код обработки обратного вызова. Например распечатать значения полей.

Запустить веб-сервис обратного вызова можно с помощью пункта Run его контекстного меню.


Данный веб-сервис будет развернут в домене по-умолчанию встроенного в JDeveloper сервера приложений WebLogic. После успешного разворачивания и запуска приложения веб-сервис станет доступен по адресу, указанному в журнале.


Данный адрес необходимо подставить на место слов http:// replace with URL of the callback service в код демонстрационного клиента веб-сервиса. Тем самым будут сформированы корректные заголовки WS-Addressing, указывающие на веб-сервис обратного вызова. После комментария Add your code to call the desired methods. необходимо поместить код вызова асинхронного метода веб-сервиса.


Запуск демонстрационного клиента осуществляется аналогично запуску веб-сервиса обратного вызова: с помощью пункта Run соответствующего контекстного меню. После запуска появится консоль, в которой будут отображены результаты вызова сервиса.


В Oracle Enterprise Manager можно наблюдать новый созданный экземпляр композита, находящийся в состоянии Running.


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


Видно, что и сам асинхронный веб-сервис и его клиент работают корректно.

Добавление в веб-сервис новой операции. Медиатор


При описании демонстрационного асинхронного веб-сервиса мы договорились о том, что он будет содержать две операции: создания новой заявки и добавление комментария. С помощью BPEL-процесса мы создали веб-сервис с операцией создания новой заявки. Теперь необходимо определить операцию добавления комментария. Существует два способа это сделать: определить данную операцию в том же BPEL-процессе, в котором определена операция создания заявки, или создать для нее отдельный BPEL-процесс. Второй подход мне кажется предпочтительнее, т.к. позволяет инкапсулировать логику одной операции в одном BPEL-процессе, что существенно упрощает дальнейшую поддержку композита. Например, при необходимости добавить новую операцию достаточно будет только добавить новый процесс.

Для связи BPEL-процессов с конечной точкой выставляемого веб-сервиса будем использовать медиатор. Медиатор - это компонент, осуществляющий валидацию, маршрутизацию и трансформацию сообщений. Раньше у корпорации Oracle бала своя собственная сервисная шина предприятия - Oracle Enterprise Service Bus, однако после поглощения компании BEA в арсенале Oracle оказалось две шины: своя и доставшаяся в наследство от BEA. Начиная с версии Oracle SOA Suite 11g, собственная ESB Oracle переименована в медиатор и используется внутри композитных приложений.

Для создания медиатора необходимо перетащить компонент Mediator с палитры в область редактирования композита. После этого откроется окно настройки медиатора, в котором будет предложено указать его наименование и выбрать шаблон взаимодействия. Для создания асинхронного веб-сервиса необходимо выбрать шаблон Asynchronous Interface.


После нажатия кнопки OK созданный медиатор отобразится в поле редактирования композита. Вместе с медиатором будет создана точка входа - TicketServiceMediator_ep, содержащая операции, определенные по-умолчанию: execute и callback.


Необходимо вручную отредактировать созданный вместе с медиатором файл, содержащий WSDL-описание веб-сервиса TicketServiceMediator_ep: определить в нем две интересующие нас операции и соответствующие обратные вызовы.


В отличие от BPEL, медиатор не использует концепцию партнерских связей. Соответственно, чтобы впоследствии к создаваемому асинхронному веб-сервису можно было сгенерировать клиент, необходимо вручную добавить в WSDL-описание тип партнерской связи, соединяющий тип порта TicketService_ptt с типом порта TicketServiceCallback_ptt.


Так как мы изменили наименование типа порта в WSDL-описании сервиса TicketServiceMediator_ep, то данные изменения необходимо отразить в файле composite.xml. Для этого можно открыть вкладку Source редактора композита и прописать новые названия типов портов у компонента service name="TicketServiceMediator_ep".


Теперь необходимо удалить созданный ранее медиатор. Фактически, мы его создавали только для того, чтобы получить шаблон WSDL-файла. К сожалению, в Oracle SOA Suite нельзя изменять интерфейс медиатора, уже связанного с каким-либо компонентом. Заодно можно удалить и используемую ранее точку входа в BPEL-процесс.


Теперь можно поместить в композит новый медиатор, причем т.к. точка входа для него уже есть - сервис TicketServiceMediator_ep, то в качестве шаблона можно использовать Define Interface Later.


После помещения медиатора в композит его необходимо соединить с точкой входа и с BPEL-процессом. При соединении медиатора с BPEL-процессом необходимо задать соответствие между операцией медиатора и операцией процесса.


Для реализации операции AddComment необходимо создать новый асинхронный BPEL-процесс, логика которого аналогична логике процесса, реализующего операцию CreateTicket. Созданный BPEL-процесс необходимо так же связать с медиатором. В результате получится следующий композит:


Заключительным действием по настройке медиатора является создание трансформаций входящих сообщений в исходящие. Для настройки трансформации используется действие Transform Using, кнопка которого напоминает две полоски, связанные крест-на-крест.


На форме данного действия можно либо выбрать существующий файл транформации, либо создать новый.


Трансформация осуществляется с помощью XSLT-преобразования. Для облегчения труда программиста в состав JDeveloper входит удобный визуальный редактор. Т.к. в данном случае трансформация тривиальная: сообщение трансформируется само в себя, то достаточно в редакторе соединить только элемент CreateTicket с элементом CreateTicket, при этом трансформация для вложенных элементов будет задана автоматически.


Однако при необходимости ее можно контролировать, например отказаться от присваивания значений каким-либо вложенным элементам.


Результат работы мастера следующий:


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


Так как при редактировании WSDL-описания веб-сервиса TicketServiceMediator_ep были прописаны типы партнерских связей, то с помощью JDeveloper'а можно сгенерировать как клиента к созданному асинхронному веб-сервису, так и веб-сервис обратных вызовов.



Дальнейший процесс создания клиента веб-сервиса аналогичен описанному выше.

После осуществления тестового вызова веб-сервиса можно проследить за работой медиатора с помощью Oracle Enterprise Manager. Если раскрыть трассировку вызовов, то можно увидеть, что сообщение с запросом прошло через трансформацию и было передано на вход операции process BPEL'я. После завершения обработки запроса сообщение обратного вызова было получено от BPEL'я и подвергнуто трансформации. Цикл обработки сообщения замкнулся.


Заключение


Асинхронный обмен сообщениями становится все более популярным подходом к интеграции информационных систем. Однако, если раньше единственным способом реализации данного подхода являлось использование очередей сообщений с помощью, например JMS, а веб-сервисы использовались исключительно для организации синхронного обмена, то с принятием ряда спецификаций, в частности WS-Addressing, ситуация стала меняться. Сейчас интеграционные платформы, такие как Oracle SOA Suite содержат средства разработки как асинхронных веб-сервисов, так и клиентов к ним, что позволит в дальнейшем отказаться при создании приложений от использования проприентарных средств, заменив их стандартизированным способом интеграции - веб-сервисами, в том числе и асинхронными. В свою очередь это существенно уменьшит затраты на включение данных приложений в информационный ландшафт предприятий.

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

14 комментариев:

slonnik комментирует...

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

slonnik комментирует...

Насчёт обртаного вызова, почему в качестве транспорта не выбрать JMS? В качестве примера Вы привели обратный вызов через неделю, как насчёт гарантированной доставки, что если через неделю сервис обратного вызова будет недоступен ?!

Pavel Samolisov комментирует...

Спасибо, опечатку поправил.

По поводу JMS. Конечно данный способ интеграции имеет право на жизнь, но что делать, если клиент нужно написать не на Java, а, например, на .NET? В случае WebLogic'а возможно это не аргумент, т.к. в его состав входит .NET-клиент для JMS очередей, но это скорее исключение, нежели правило.

По поводу гарантированной доставки. Если сервиса обратного вызова нет, но в заголовке запроса его адрес был указан, то при попытке отправить данный вызов будет сгенерирована ошибка. Ошибку можно перехватить с помощью SOA Suite Error Hospital и обработать, например повторить вызов через некоторое время.

Справедливости ради стоит заметить еще одно преимущество JMS: распределенные транзакции. С помощью SOAP их тоже можно реализовать, используя спецификацию WS-AtomicTransaction, но это гораздо сложнее и, на мой взгляд, менее надежно.

intr13 комментирует...

> Существует два подхода к обеспечению асинхронного взаимодействия с помощью механизма веб-сервисов:

Есть еще один подход, длинные http запросы. Например, у Jetty есть такая штука как Continuation. Но стоит отметить что это не совсем асинхронное взаимодействие, по крайней мере клиент ждет завершения операции. С другой стороны это проще и прозрачнее, хотя требует определенных усилий на сервере.

Pavel Samolisov комментирует...

>> Клиент ждет завершения операции.
В JAX-WS можно создать асинхронный клиент к синхронному веб-сервису. При вызове будет использоваться механизм Future. Похожий механизм реализован и в Eclipse Communication Framework.

Артем Зацаринный комментирует...

Скажите, правильно ли я понимаю, что при реализации Web-сервиса, с использованием механизма обратного вызова, клиент (потребитель сервиса) после отправки запроса поставщику сервиса, фактически выполняет функцию Web-сервера, ожидая ответа от поставщика.
И еще вопрос: объясните мне, пожалуйста, чем отличается "обычный" poling от long poling'а.

Pavel Samolisov комментирует...

Да, все верно. Клиент по сути так же является и веб-сервисом обратного вызова.

На второй вопрос ответить не могу, не знаю что такое long poling.

Олег Архипов комментирует...

Павел, замечательный пост.
Помогите решить задачу.
Условия (SOA Suite 11.1.1.4):
Есть два комопзита Parent и Child.
В каждом из них по асинхронному BPEL процессу: ParentProcess и ChildProcess.
В процессе ParentProcess асинхронный вызов ChildProcess (из композита Child).
ChildProcess штука долгая, ждет там всяких событий и обрабатывает их.. после чего отвечает вызвашему его клиенту.
ChildProcess реализован так что сохраняет свое состояние и может быть легко перезпущен.
Окружение продуктивное, процессы долгоиграющие. Изменения в композитах (практически, формально) запрещены.
Задача.
После удачного вызова ChildProcess'а ParentProcess переходит в ожидание callback'a.
Но ChildProcess скоропостижно исчезает. Допустим аборитится или падает SOAINFRA.. т.е. не перехватывается такое никак.
Возможно ли (если да то как) перезпустить процесс ChildProcess (скажем с помощью SOAPUI), так чтобы он (ChildProcess) потом возвращая ответ,
ответил именно нужному процессу ParentProcess.

Я наивно предпологал что здесь будет достаточно использовать Corrleation Set'ы.
Но этого оказалось недостаточно. Процесс ChildProcess балогополучно завершается, но ParentProcess этого "не слышит".
Я почти уверен, что надо как-то правильно задать replyTo/Address при перезпуске ChildProcess.
Но что туда передовать я не смог выкопать.

Если есть какие-нибудь соображения, буду очень признателен.
Спасибо.

Олег.

Pavel Samolisov комментирует...

Здравствуйте.

Если я правильно понял, проблема в том, что ChildProcess падает и, соответственно, не успевает отправить обратный вызов. Соответственно, ParentProcess'у просто нечего принимать, он будет вечно ждать обратного вызова.

Здесь нужно сделать следующее:
1. Определить Catch (или CatchAll) блок в ChildProcess, в котором отправлять ParentProcess'у нотификацию об ошибке.

2. Воспользоваться SOA Suite Error Hospital - довольно мощным средством обработки ошибок. Обработчики ошибок описываются декларативно в файлах fault-bindings.xml и fault-policies.xml. С их помощью можно реализовать следующую стратегию: если ParentProcess недоступен (а такое ведь тоже может быть), то повторять вызовы некоторое время, а затем сделать ошибку восстанавливаемой (recoveriable). Такие ошибки требуют вмешательства администратора и отображаются в интерфейсе Enterprise Manager'а. Администратор может повторить вызов, отклонить его, поменять текст сообщения и т.д. Рекомендую разобраться с данной темой.

Материалы:
- глава 14 книги Oracle SOA Suite 11gR1 Developer Guide (я ее перевел, но не уверен, что стоит выкладывать);
- Using Fault Handling in a BPEL Process.

P.S. К сожалению, придется править композиты, поэтому смотрите, подходят ли вам данные способы.

Олег Архипов комментирует...

Спасибо, Павел.
Но это не совсем то.
У меня там довольно мощная обработка ошибок с использованием HumanWorkflow и возможностью recovery. Но дело как раз в том что "падает" так что ничто это не помогает. Например, админы перегузили БД где SOAINFRA живет. Именно такие дочерние ингода(!) встают колом. В общем точно такая же ситуация как если бы процессу сделали abort.

Олег.

Pavel Samolisov комментирует...

Ну abort насколько я знаю перехватить нельзя. Я обычно строю процессы как набор короткоживущих композитов, связанных с помощью JMS. В таком случае многие проблемы решаются главной организацией транзакций, в частности, откатом транзакции после ошибки.

А с какой ошибкой у вас валится дочерний процесс?

Pavel Samolisov комментирует...

... правильной организацией транзакций... дальше - по тексту.

Dmitrii комментирует...

>При этом будет создан новый экземпляр композита, который будет находиться в состоянии Running до тех пор, пока в очереди ответов от внешнего приложения не появится соответствующее сообщение.
А можно ли настроить так, что новый экземпляр не будет создан, если в запросе не было адреса для ответа?

Pavel Samolisov комментирует...

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

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

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