вторник, 20 ноября 2012 г.

Асинхронный веб-сервис на Oracle Service Bus

Обеспечить асинхронное взаимодействие с веб-сервисом по протоколу HTTP можно не только с помощью Oracle SOA Suite, но и с помощью Oracle Service Bus. Основная сложность при этом заключается в том, что Oracle Service Bus не содержит встроенных средств сохранения состояния, поэтому необходимо будет каким-то образом сохранить во внешней памяти URL сервиса обратного вызова и идентификатор сообщения, на которое мы отвечаем. Так же нам нужно будет самостоятельно сформировать заголовок ответного SOAP-сообщения, соответствующий спецификации WS-Addressing. Давайте посмотрим как это можно сделать.


Сохранить во внешней памяти URL сервиса обратного вызова, т.е. того сервиса, на который нам необходимо отправить асинхронный ответ после завершения работы, и идентификатор поступившего сообщения можно несколькими способами. Один из них - использовать между OSB и реализацией сервиса JMS-очередь и выставить соответствующие дополнительные свойства записываемому в нее сообщению. Аналогично для передачи ответа от реализации сервиса к OSB использовать другую JMS-очередь - очередь ответов. Соответственно, на шине должны присутствовать прокси-сервис, принимающий запрос по протоколу HTTP, извлекающий из его заголовка URL сервиса обратного вызова и идентификатор сообщения и помещающий данный запрос в очередь запросов, а так же прокси-сервис, слушающий очередь ответов и формирующий сообщение для сервиса обратного вызова. При этом в данный прокси-сервис необходимо каким-то образом передать сохраненные в первой очереди - очереди запросов - URL сервиса обратного вызова и идентификатор сообщения-запроса. Сделать это можно с помощью тех же дополнительных свойств JMS-сообщения, помещаемого реализацией сервиса в очередь ответов. Получается, что данная схема накладывает одно ограничение на реализацию сервиса - та должна перекладывать значения дополнительных свойств JMS-сообщения запроса в JMS-сообщение ответа.

Логика первого прокси-сервиса - AsyncProxyService проста. Данный прокси-сервис с помощью HTTP-транспорта принимает SOAP-запросы. Затем принятое сообщение с помощью бизнес-сервиса WriteRequestBusinessService помещается в соответствующую JMS-очередь.


Наиболее интересная задача, решаемая данным прокси-сервисов - формирование дополнительных свойств JMS-сообщения: MessageId - идентификатор принятого сообщения-запроса и Address - URL сервиса обратного вызова. Данные свойства формируются с помощью действия Transport Header.


Извлекаются значения данных свойств из соответствующих полей заголовка SOAP-сообщения: MessageId из поля MessageID:

  1. $header/wsa05:MessageID/text()

а Address - из поля ReplyTo/Address:

  1. $header/wsa05:ReplyTo/wsa05:Address/text()

В приведенных выражениях префикс wsa05 соответствует пространству имен http://www.w3.org/2005/08/addressing.

Рассмотрим пример:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:asy="http://www.example.org/AsyncService/">

  <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">

    <wsa:Action>http://www.example.org/AsyncService/AsyncService/demoOperationRequest</wsa:Action>

    <wsa:ReplyTo>

       <wsa:Address>http://localhost:8088/mockAsyncServiceCallbackBinding</wsa:Address>

    </wsa:ReplyTo>

    <wsa:MessageID>uuid:8192b9b4-e268-4f65-a9e8-c1e798eb706f</wsa:MessageID>

  </soapenv:Header>

  <soapenv:Body>

    <asy:demoOperation>

      <asy:id>120</asy:id>

      <asy:name>Demo</asy:name>

      <asy:action>Log</asy:action>

    </asy:demoOperation>

  </soapenv:Body>

</soapenv:Envelope>

 

При получении данного запроса прокси-сервис AsyncProxyService сформирует следующее JMS-сообщение:


Для чтения JMS-очереди ответов и формирования обратного вызова используется прокси-сервис AsyncCallbackProxyService. Данный прокси-сервис формирует соответствующие стандарту WS-Addressing поля заголовка SOAP-сообщения и отправляет данное сообщение на указанный в дополнительном свойстве Address JMS-сообщения URL сервиса обратного вызова.


Формирование заголовка SOAP-сообщения осуществляется с помощью действия Replace. Необходимо сформировать заголовок, содержащий следующие поля:

  • MessageID - идентификатор сообщения обратного вызова. Формируется с помощью встроенной в OSB функции fn-bea:uuid().

  • RelatesTo - идентификатор сообщения, на которое отвечает данное. Извлекается из дополнительного заголовка MessageId: {data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='MessageId']/@value)}.

  • ReplyTo/Address - адрес сервиса для ответа на сообщение обратного вызова. Если продолжение диалога не предусмотрено, то можно указать анонимный адрес: http://www.w3.org/2005/08/addressing/anonymous.


Целиком выражение для формирования заголовка будет выглядеть следующим образом:

<soap-env:Header>

    <wsa05:MessageID>{fn:concat('uuid:', fn-bea:uuid())}</wsa05:MessageID>

    <wsa05:RelatesTo>{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='MessageId']/@value)}</wsa05:RelatesTo>

    <wsa05:ReplyTo>

        <wsa05:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa05:Address>

    </wsa05:ReplyTo>

</soap-env:Header>

Для отправки ответа по адресу, сохраненному в поле Address, используется действие Routing Options. Данное действие позволяет изменять адрес, на который отправляется ответ, параметры участия сервиса в транзакции (Quality of Service, QoS), режим обмена (Request или Request-Response), интервал между повторами и количество повторов в случае сбоя, а так же приоритет сообщения.


В данном случае нам необходимо изменить параметр URI, подставив в него значение поля Address, получить которое можно с помощью следующего выражения:

$inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='Address']/@value

При этом очень важно помнить, чтобы дополнительные свойства JMS-сообщения были доступны в прокси-сервисе, необходимо выставить значение поля Get All Headers, расположенного на вкладке Transport, данного сервиса равным All:


На этом разработку асинхронного сервиса на OSB можно считать законченной. Протестировать его можно с помощью SOAP UI, предварительно создав там эмулятор сервиса обратного вызова. При этом подразумевается, что за OSB стоит некоторая работающая реализация асинхронного сервиса, т.е. сервиса считывающего сообщения из JMS-очереди запросов, обрабатывающего их и помещающего ответы в очередь ответов. При отправке запроса с помощью SOAP UI необходимо правильно заполнить соответствующие поля на вкладке WS-Addressing related settings, особенно поле Reply To.


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


Текст ответа при этом будет содержать все необходимые поля WS-Addressing, позволяющие произвести его корреляцию с отправленным запросом.

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

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

  <soapenv:Header>

    <add:MessageID xmlns:add="http://www.w3.org/2005/08/addressing">uuid:975eb75b-9604-4762-ba25-e3326b97d619</add:MessageID>

    <add:RelatesTo xmlns:add="http://www.w3.org/2005/08/addressing">uuid:7676da44-b1c4-47d5-ba5b-f476a335bf86</add:RelatesTo>

    <add:ReplyTo xmlns:add="http://www.w3.org/2005/08/addressing">

      <add:Address>http://www.w3.org/2005/08/addressing/anonymous</add:Address>

    </add:ReplyTo>

  </soapenv:Header>

  <soapenv:Body xmlns:asy="http://www.example.org/AsyncService/">

    <asy:demoOperationResponse>

      <asy:result>true</asy:result>

    </asy:demoOperationResponse>

   </soapenv:Body>

</soapenv:Envelope>

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

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

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

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