Содержание


ESB на практике

Создание Enterprise Service Bus в WebSphere Application Server V6

Существует множество способов реализации ESB с продуктами IBM и продуктами промежуточного уровня других производителей. IBM WebSphere Application Server Version 6 предоставляет очень интересную платформу для создания ESB. [Reinitz1] представил WebSphere Messaging Resources (известную также под названием SIBus), доступную в Version 6. SIBus может быть использована для построения разных топологий ESB. В этой статье рассмотрены некоторые концепции и терминология, а в статье [Reinitz2] описывается процесс настройки, необходимой для разрешения SIBus взаимодействовать с Web-службами SOAP/HTTP. Дополнительную информацию по SIBus можно найти в WebSphere Application Server V6 InfoCenter под заголовком Service Integration.

Двумя наиболее часто упоминаемыми возможностями ESB являются преобразование сообщений и их маршрутизация. Данная статья основывается на материалах, представленных в [Reinitz1] и [Reinitz2], и рассматривает процесс создания экземпляра ESB, реализующей преобразование сообщений и маршрутизацию.

На рисунке 1 изображен сценарий, который мы будем реализовывать. На рисунке указан инициатор запроса службы, который использует службу (возможно, при помощи совместимой с JAX-RPC прокси-службы), предлагающую операцию publish с изображенной сигнатурой. На рисунке также изображен провайдер службы, предлагающий операцию notify с указанной сигнатурой. Вы можете заметить несоответствие между тем, что хочет получить инициатор запроса, и тем, что в действительности может предложить провайдер. Роль ESB, которую мы создадим, заключается в выполнении необходимого преобразования, для того чтобы инициатор запроса смог успешно взаимодействовать с провайдером службы. Более конкретно – наша ESB будет выполнять WSDL-отображение portType.

Рисунок 1. Сценарий отображения PortType
Рисунок 1. Сценарий отображения PortType
Рисунок 1. Сценарий отображения PortType

Анализ решения

Прежде всего, проанализируем сценарий, чтобы понять, какого типа топологию SIBus (здесь и далее называемую шиной) мы должны создать. Исходя из начального анализа, мы должны:

  1. Получить SOAP/HTTP-запрос в шину от инициатора запроса. Для этого мы должны создать на шине входящую службу, используя WSDL инициатора запроса. Для определения входящей службы мы, прежде всего, должны установить приложение оконечного прослушивателя SOAP/HTTP, как описано в [Reinitz2];
  2. Преобразовать запрос из формата инициатора в формат провайдера. Это потребует создания для адресата объекта-посредника, обрабатывающего сообщение (в данном сценарии преобразующего сообщение). Мы должны создать и установить объект-посредник или список обработчиков-посредников для выполнения преобразования; в данном сценарии мы будем использовать для этого XSLT;
  3. Получить запрос из шины к провайдеру. Для этого нам нужно создать исходящую службу, используя WSDL провайдера. Предварительно мы должны установить компонент, вызывающий службу по SOAP/HTTP, который устанавливается с оконечным прослушивателем;
  4. Преобразовать ответ из формы провайдера в форму инициатора запроса. И хотя в обоих случаях ответы являются пустыми, они все равно ожидаются. Ответы имеют различия в пространствах имен, которые должны учитываться. Однако, поскольку нет реального содержимого, мы схитрим и вставим в ответное сообщение жестко закодированное сообщение для инициатора запроса. Даже это потребует создания и установки списка обработчиков объекта-посредника для обработки ответа.

Этот анализ дает хорошую общую картину того, что мы должны сделать. Однако мы сделаем более глубокий анализ, основанный на более детальном понимании шины. Шаг 1 требует создания входящей службы. Как показано на рисунке 2, входящая служба состоит из оконечного прослушивателя, который перенаправляет сообщения адресату входящей службы (показан желтым прямоугольником). Адресат входящей службы должен существовать до создания входящей службы. При создании входящей службы показанный на рисунке адресат ответов создастся автоматически. Этот адресат ответов используется для перенаправления ответов назад к инициатору запроса через оконечный прослушиватель. Существует один адресат ответов по умолчанию для всех входящих служб, создаваемый в консоли администратора сервера приложений.

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

Рисунок 2. Входящая служба
Рисунок 2. Входящая служба
Рисунок 2. Входящая служба

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

Рисунок 3. Исходящая служба
Рисунок 3. Исходящая служба
Рисунок 3. Исходящая служба

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

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

Как конечный результат мы должны создать рабочую топологию, подобную показанной на рисунке 4. Прежде всего, мы создадим исходящую службу, что автоматически создаст адресаты исходящей службы и исходящего порта. Затем мы создадим входящую службу, указывая адресат исходящей службы в качестве целевого адресата входящей службы. Мы можем добавить посредника к адресату исходящей службы для преобразования ответного сообщения (посредник изображен синим шестиугольником). Мы должны вручную создать адресат для перехвата ответов. Мы можем добавить к нему посредника для преобразования ответов. Важно понимать, что создание объекта-посредника, связанного с адресатом перехвата ответов, не влияет на маршрут по умолчанию ответов от активатора службы к адресату ответов по умолчанию. Для того чтобы принудить ответы сначала идти к адресату перехвата ответов, мы должны изменить обратный маршрутный путь в объекте-посреднике, связанном с адресатом исходящей службы.

Рисунок 4. Рабочая топология
Рисунок 4. Рабочая топология
Рисунок 4. Рабочая топология

Есть еще одно последнее, но важное, решение, которое мы должны принять. Мы могли бы создать изображенную на рисунке 4 топологию в существующем экземпляре SIBus, либо создать новый экземпляр. Для нашего простого сценария ESB мы создадим новую шину, поскольку нам не надо взаимодействовать с какими-либо дополнительными артефактами шины.

Мы завершили наш анализ и теперь можем приступить к построению топологии! В этой статье мы будем выполнять все необходимые действия для реализации нашего решения в поставляемых с WebSphere Test Environment инструментальных средствах разработки Rational V6 (либо версии Application Developer, либо версии Software Architect). Эти средства позволяют использовать полностью функциональный WebSphere Application Server version 6 и пользоваться всеми преимуществами интегрированной среды разработки. В данном случае мы будем использовать средства для создания топологии (рисунок 4), разработки объектов-посредников и тестирования всей системы. Предполагается, что вы знакомы с программами Rational, и мы не будем останавливаться на деталях процесса разработки, не относящихся к SIBus.

Создание исходящей службы

Перед созданием исходящей службы мы должны создать новый экземпляр шины и назвать его TypeMap. Смотрите в статье [Reinitz1] описание процедуры создания нового экземпляра шины в консоли администратора.

Для создания исходящей службы мы должны указать в консоли администратора WSDL-документ, описывающий провайдера службы. В листинге 1 приведены важные участки WSDL-файла для службы SubscriberS1T1Service с portType SubscriberS1T1. Он составлен с учетом передового опыта в соответствии с литеральной формой свернутых документов. Также использован современный подход – импорт схемы для параметров вместо прямого встраивания схемы. Схема приведена в листинге 2.

Листинг 1. WSDL для SubscriberS1T1Service
<wsdl:definitions xmlns:impl="http://pubsubhub.ibm.com"
    xmlns:intf="http://pubsubhub.ibm.com" ... >

 <wsdl:types>
  <schema xmlns:sub="http://www.sub1.com/sub1doc" ... >
   <import namespace="http://www.sub1.com/sub1doc" 
      schemaLocation="sub1Topic1.xsd"/>
   <element name="notify">
    <complexType>
       <sequence>
        <element name="doc" nillable="true" type="sub:Sub1Topic1Doc"/>  
       </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>

 <wsdl:message name="notifyRequest">
      <wsdl:part element="impl:notify" name="parameters"/>
 </wsdl:message>

 <wsdl:portType name="SubscriberS1T1">
      <wsdl:operation name="notify">
         <wsdl:input message="impl:notifyRequest" 
        name="notifyRequest"/>
         <wsdl:output message="impl:notifyResponse" 
        name="notifyResponse"/> 
      </wsdl:operation>
   </wsdl:portType>
  ...
 <wsdl:service name="SubscriberS1T1Service">
      <wsdl:port binding="impl:SubscriberS1T1SoapBinding" 
    name="SubscriberS1T1">
         ...
      </wsdl:port>
 </wsdl:service>
Листинг 2. Схема для SubscriberS1T1Service
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.sub1.com/sub1doc" ... >
  <complexType name="Sub1Topic1Doc">
    <sequence>
      <element name="firstName" type="string"/>
      <element name="lastName" type="string"/>
    </sequence>
  </complexType>
  <element name="Sub1Topic1Doc" 
     type="tns:Sub1Topic1Doc"></element>
</schema>

Для создания исходящей службы выполните следующие дейтвия:

  1. В консоли администратора выберите Service integration => Buses => TypeMap => Outbound Services => New.
  2. В диалоговом окне "New Outbound Service" (Новая исходящая служба) укажите расположение WSDL-документа, который определяет провайдера службы, представляющего исходящую службу. Мы укажем URL, как показано на рисунке.
  3. Нажмите Next.
    Рисунок 5. Создание исходящей службы, шаг 1
    Рисунок 5. Создание исходящей службы, шаг 1
    Рисунок 5. Создание исходящей службы, шаг 1
  4. В следующем диалоговом окне мы должны выбрать одну службу, поскольку в документе может быть определено несколько служб. WSDL для SubscriberS1T1Service содержит только одну службу, поэтому выберите вариант по умолчанию. Нажмите Next.
  5. В диалоговом окне "Select Ports" (Выбор портов) выберите порт, через который будут передаваться сообщения. Опять же, хотя могут отображаться несколько портов, SubscriberS1T1Service содержит только один, который и должен быть выбран по умолчанию. Нажмите Next.
    Рисунок 6. Создание исходящей службы, шаг 3
    Рисунок 6. Создание исходящей службы, шаг 3
    Рисунок 6. Создание исходящей службы, шаг 3
  6. В диалоговом окне "Name the Outbound Service and Destinations" (Имена исходящей службы и адресатов), показанном на рисунке 7, выполните следующие действия:
    1. Введите имя исходящей службы в поле Outbound service name, показанное на рисунке. Это имя для удобства администрирования и может быть любым по вашему желанию, но оно должно быть уникальным для экземпляра шины.
    2. Введите Service destination name (Имя адресата службы). Консоль администратора создает имя по умолчанию, которое является комбинацией пространства имен WSDL и имени службы. В данном случае используйте имя по умолчанию.
    3. Наконец, введите имена адресатов исходящего порта в поле Port destination name. Опять же, консоль администратора выбирает имена по умолчанию, являющиеся комбинацией целевого пространства имен WSDL, имени службы и имени порта. Для нашего случая подходит имя для единственного порта.
    4. Нажмите Next.
      Рисунок 7. Создание исходящей службы, шаг 4
      Рисунок 7. Создание исходящей службы, шаг 4
      Рисунок 7. Создание исходящей службы, шаг 4
  7. В следующем диалоговом окне нажмите Finish.

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

Создание входящей службы

Для определения входящей службы мы должны указать в консоли администратора WSDL-документ, описывающий ожидаемый инициатором запроса службы интерфейс. В листинге 3 приведены важные участки исходного кода WSDL-файла для службы PublisherS1T1Service c portType PublisherS1T1. Он составлен с учетом передового опыта в соответствии с литеральной формой свернутых документов. Также использован современный подход импорта схемы для параметров вместо прямого встраивания схемы. Схема приведена в листинге 4. Сравнивая WSDL для SubscriberS1T1Service и для PublisherS1T1Service, вы можете заметить, что нам необходимо преобразовать два аспекта сообщения запроса: operation и body, включая пространство имен.

Листинг 3. WSDL для PublisherP1T1Service
<wsdl:definitions xmlns:impl="http://pubsubhub.ibm.com" 
    xmlns:intf="http://pubsubhub.ibm.com" ... >
 <wsdl:types>
  <schema xmlns:sub="http://www.sub1.com/sub1doc" ... >
    <import namespace="http://www.pub1.com/pub1doc" 
        schemaLocation="pub1Topic1.xsd"/>
 <element name="publish">
    <complexType>
       <sequence>
        <element name="doc" nillable="true" type="sub:Pub1Topic1Doc"/>  
       </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>

<wsdl:message name="publishRequest">
      <wsdl:part element="impl:publish" name="parameters"/>
 </wsdl:message>
 <wsdl:portType name="PublisherS1T1">
      <wsdl:operation name="publish">
         <wsdl:input message="impl:publishRequest" name="publishRequest"/>
         <wsdl:output message="impl:publishResponse" name="publishResponse"/> 
      </wsdl:operation>
  </wsdl:portType>
  ...
   <wsdl:service name="PublisherP1T1Service">
      <wsdl:port binding="impl:PublisherP1T1SoapBinding" name="PublisherP1T1">
         ...
      </wsdl:port>
  </wsdl:service>
Листинг 4. Схема PublisherP1T1Service
<?xml version="1.0" encoding="UTF-8"?>
<schema 
 targetNamespace="http://www.pub1.com/pub1doc" ... >
  <complexType name="Pub1Topic1Doc">
    <sequence>
      <element name="firstName" type="string"/>
      <element name="lastName" type="string"/>
      <element name="address" type="string"/>
   </sequence>
  </complexType>
  <element name="Pub1Topic1Doc" type="tns:Pub1Topic1Doc"></element>
</schema>

Для создания входящей службы выполните следующие действия:

  1. В консоли администратора выберите Service integration => Buses => TypeMap => Inbound Services => New.
  2. В диалоговом окне "New Inbound Service" (Новая входящая служба) мы должны указать адресат входящей службы и расположение WSDL-документа, определяющего ожидаемый инициатором запроса интерфейс. Как указывалось выше, мы выбираем адресат исходящей службы, созданный автоматически при создании исходящей службы, и указываем URL для WSDL, как показано ниже.
  3. Нажмите Next.
    Рисунок 8. Определение входящей службы, шаг 1
    Рисунок 8. Определение входящей службы, шаг 1
    Рисунок 8. Определение входящей службы, шаг 1
  4. В следующем диалоговом окне мы должны выбрать службу. WSDL для PublisherS1T1Service содержит только одну службу, поэтому мы будем использовать указанную по умолчанию. Нажмите Next.
  5. В следующем диалоговом окне, показанном на рисунке 9, выполните следующие действия:
    1. Введите имя входящей службы в поле Inbound service name. Это имя необходимо для удобства администрирования и может быть любым по вашему желанию. Консоль администратора предлагает выбрать имя по умолчанию, являющееся комбинацией информации о связывании, имени адресата и других имен; оно является уникальным, но обычно длинным и малопонятным. В нашем случае мы будем использовать имя службы.
    2. Выберите оконечный прослушиватель и нажмите Next.
      Рисунок 9. Определение входящей службы, шаг 3
      Рисунок 9. Определение входящей службы, шаг 3
      Рисунок 9. Определение входящей службы, шаг 3
    3. В следующем диалоговом окне нажмите Finish.

Мы создали входящую службу.

Небольшие поправки

Из-за ограничений в Application Server V6.0.1 и более ранних версиях прогрессивный опыт использования отдельной схемы для параметров в WSDL-документе приводит к возникновению проблем, которые можно заметить только во время выполнения. Консоль администратора автоматически загружает WSDL-документы в SDO-репозиторий, используемый SIBus для обработки сообщений Web-служб (дополнительную информацию по SDO-репозиторию можно найти в статье [Reinitz2] и в Application Server InfoCenter). Однако она не загружает в репозиторий отдельные схемы, на которые ссылаются WSDL-документы. Это означает, что мы должны загружать схемы вручную при помощи программы командной строки wsadmin. Обычная форма использования команд wsadmin такова:

set sdoRepBean [$AdminControl queryNames *:*, type=SdoRepository]
puts [$AdminControl invoke $sdoRepBean importResource 
{"resourceName" "sourceFileName"}]

Прежде всего, откройте командную оболочку и перейдите в каталог <install_path>/profiles/<profile>/bin, где <install_path> - это путь, куда был установлен Application Server, а <profile> ссылается на используемый вами профиль (возможно по умолчанию). Введите wsadmin для запуска программы; затем выполните набор команд, приведенных выше. Следующие команды используются для загрузки схем для двух служб через указание имени файла:

puts [$AdminControl invoke $sdoRepBean importResource {"pub1Topic1.xsd"
"C:/workspace/MyServices/WebContent/wsdl/com/ibm/pubsubhub/pub1Topic1.xsd"}]
puts [$AdminControl invoke $sdoRepBean importResource {"sub1Topic1.xsd" 
"C:/workspace/MyServices/WebContent/wsdl/com/ibm/pubsubhub/sub1Topic1.xsd"}]

Возможно, лучше было бы использовать URL для указания схемы, но использованные выше месторасположения в файловой системе, кажется, работают. Эти команды нужно выполнить только один раз. Если вы хотите определить, есть ли уже что-то в SDO-репозитории, то можете выполнить следующую команду:

puts [$AdminControl invoke $sdoRepBean listResources]

Создание адресата перехвата ответов

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

  1. В консоли администратора выберите Service integration => Buses => TypeMap => Destinations => New.
  2. В диалоговом окне "Create New Destination" (Создание нового адресата) проверьте, что выбрана Queue, и нажмите Next.
    Рисунок 10. Создание адресата
    Рисунок 10. Создание адресата
    Рисунок 10. Создание адресата
  3. В диалоговом окне "Create New Queue" (Создание новой очереди) введите FixReply в поле Identifier. Добавьте необязательное описание и нажмите Next.
    Рисунок 11. Создание адресата, шаг 1
    Рисунок 11. Создание адресата, шаг 1
    Рисунок 11. Создание адресата, шаг 1
  4. В следующем диалоговом окне вы должны назначить очередь участнику шины. Для нашего сценария существует только один, поэтому просто нажмите Next.
  5. В следующем диалоговом окне нажмите Finish.

Мы создали адресат перехвата

Объект-посредник для адресата входящей службы

Теперь мы должны преобразовать входящее сообщение из формы, подходящей для PublisherP1T1Service, в форму, подходящую для SubscriberS1T1Service. Для этого мы свяжем объект-посредник с адресатом входящей службы, как показано на рисунке 4. Объект-посредник состоит из списка обработчиков-посредников; в нашем случае список содержит только один обработчик, рассматривающийся в этом разделе. Мы должны разработать обработчик-посредник для преобразования сообщения из одной формы в другую. Поскольку это требует преобразования XML-to-XML, мы будем использовать XSLT для выполнения реального преобразования. В статье [Reinitz3] приведена более детальная информация по написанию и развертыванию объектов-посредников.

XSLT должен сделать две вещи. Во-первых, он должен иметь дело с SOAP-конвертом сообщения; это означает игнорирование конверта входящего сообщения и добавление конверта вокруг исходящего сообщения. Во-вторых, он должен преобразовывать тело сообщения; это означает преобразование документа, описанного типом Pub1Topic1Doc в пространстве имен http://www.pub1.com/pub1doc, в документ, описанный типом Sub1Topic1Doc в пространстве имен http://www.sub1.com/sub1doc. В инструментальные средства Rational входит программа отображения XML-to-XML, которая может помочь при создании XSLT, приведенного ниже.

Листинг 5. XSLT для преобразования сообщения запроса
<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform
    version="1.0" xmlns:xalan=http://xml.apache.org/xslt
    xmlns:sub1="http://www.sub1.com/sub1doc" 
    xmlns:pub1="http://www.pub1.com/pub1doc" 
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:q0="http://pubsubhub.ibm.com" exclude-result-prefixes="pub1">
  <xsl:output method="xml" encoding="UTF-8" indent="yes" 
      xalan:indent-amount="2" /> 
  <xsl:strip-space elements="*" /> 
  <xsl:template match="soapenv:Envelope/soapenv:Body/q0:publish">
    <soapenv:Envelope 
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:q0="http://pubsubhub.ibm.com" 
  xmlns:q1="http://www.sub1.com/sub1doc" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:tns="http://www.sub1.com/sub1doc" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
        <q0:notify>
          <q0:doc>
            <sub1:firstName>
              <xsl:value-of select="pub1:doc/pub1:firstName/text()" /> 
            </sub1:firstName>
            <sub1:lastName>
              <xsl:value-of select="pub1:doc/pub1:lastName/text()" /> 
            </sub1:lastName>
          </q0:doc>
        </q0:notify>
      </soapenv:Body>
    </soapenv:Envelope>
  </xsl:template>
 </xsl:stylesheet>

Обработчики-посредники выполняются в EJB-контейнере. Мы будем использовать программы Rational для разработки обработчиков-посредников и для развертывания их на шине.

Сначала создайте EJB-проект. В этом проекте создайте Java-класс (не EJB!) с именем P1S1Transform, расширяющий интерфейс com.ibm.websphere.sib.mediation.handler.MediationHandler. В листинге 6 приведена структура обработчика-посредника P1S1Transform, используемая для преобразования сообщения. Сначала он получает два свойства контекста, которые будут установлены в адресате входящей/исходящей службы; эти свойства дадут возможность настройки поведения посредника в зависимости от адресата, для которого посредник работает. Свойство traceOn указывает обработчику записывать информацию в журнал, или, при работе в программе разработки, на консоль. Свойство xsltName предоставляет обработчику имя файла, содержащего XSLT-определение.

Листинг 6. Обработчик-посредник P1S1Tranform
public class P1S1Transform implements MediationHandler {

    protected String xsltName = null;
    protected boolean traceOn = false;
    protected SIMessageContext siMC = null;
    protected SIMessage message = null;
    protected String defaultOutputFormatString = SIApiConstants.JMS_FORMAT_BYTES;

    public boolean handle(MessageContext messageContext) throws
     	MessageContextException {

	   String mediationName = "P1S1Transform";
	   // получить необходимые свойства контекста
	   traceOn = ((Boolean)messageContext.getProperty
		("traceOn")).booleanValue();
	   xsltName = (String) messageContext.getProperty
		("xsltFileName");	
	   if (traceOn) {
	    	System.out.println("**** Entering " + 
		  mediationName + " Mediation ****");
	    System.out.println
	    	  ("on Destination - " 	+ 
		  ((SIMessageContextProxyImpl) 
		  messageContext).getSession().getDestinationName());
	    System.out.println
	    	  ("reading properties - traceOn, xsltFileName");
	    System.out.println
                  ("traceOn is " + traceOn + "  xsltFileName is " + xsltName);
		}
		&
	}
}

В листинге 7 приведены важные аспекты преобразования сообщения. Сначала мы получаем сообщение в виде массива байт – необходимая входная информация для XSLT-преобразования. Затем мы получаем XSLT-определение и преобразуем сообщение при помощи javax.xml.transform. Детали использования преобразователя не показаны. Потом мы замещаем текущее сообщение преобразованным. Модифицируем сообщение из формата для входящей службы в формат исходящей службы. Эта операция заслуживает небольшого пояснения.

Листинг 7. Обработчик-посредник P1S1Tranform, код преобразования
// Преобразование MessageContext в SIMessageContext
	siMC = (SIMessageContext) messageContext;
	// Извлечь сообщение из сессии
	message = siMC.getSIMessage();
		
	// Преобразовать сообщение  
	try {
		DataGraph graph = 
            message.getNewDataGraph(SIApiConstants.JMS_FORMAT_BYTES);
		DataObject body = graph.getRootObject();
		if (body.isSet("data")) {
			// Получить байты
			byte[] payload = body.getBytes("data/value");

			// Преобразовать байты из формата P1 в формат S1
			String transform = readFileAsString(xsltName);
			payload = XSLTTransform.transform(payload, transform);

		       // Заменить payload в data graph
		       body.setBytes("data/value",payload);
		        
		       // Заменить содержимое сообщения новым
		       String outputFormatString = 
"SOAP:dest:TypeMap:http://pubsubhub.ibm.com:SubscriberS1T1Service,
http://pubsubhub.ibm.com,SubscriberS1T1Service,SubscriberS1T1";
		       message.setDataGraph(graph, SIApiConstants.JMS_FORMAT_BYTES);
		       DataGraph newGraph = message.getNewDataGraph(outputFormatString);
		       message.setDataGraph(newGraph, outputFormatString);
			
		} else {
			...
		}
			
		return true;
	} catch (Exception e) {
		...
	}

Для понимания формата сообщения шина использует форматную строку. Более подробно она описана в Application Server InfoCenter. Форматная строка указывает, например, WSDL и схему, определяющую сообщение. Вы можете представлять формантную строку как указатель в SDO-репозиторий. Шина использует форматную строку для создания SDO, который может использовать объект-посредник для проверки и изменения сообщения. Для SOAP-сообщений синтаксис форматной строки следующий:

SOAP:<SDO repository key>,<service namespace>,<service name>,<port name>


Формат <SDO repository key> для входящей/исходящей службы:

in:<busname>:<inbound service name>
dest:<busname>:<service destination name>


То есть, для помещения сообщения в правильный формат для исходящей службы P1S1Transform должен использовать следующую форматную строку:

SOAP:dest:TypeMap:http://pubsubhub.ibm.com:SubscriberS1T1Service,
http://pubsubhub.ibm.com,SubscriberS1T1Service,SubscriberS1T1

Помните о том, что мы должны модифицировать обратный маршрутный путь (путь прохождения ответа) таким образом, чтобы ответ попадал к адресату перехвата ответов перед адресатом ответов по умолчанию. В листинге 8 приведен код, который это делает. Он размещен во фрагменте кода из листинга 7 прямо перед оператором catch. Сначала код получает путь ответов (или обратный маршрутный путь). Затем вставляет адресат перехвата ответов, называемый FixReply, в путь перед адресатом ответов по умолчанию. Наконец, он заменяет обратный маршрутный путь сообщения измененным путем.

Листинг 8. Обработчик-посредник P1S1Tranform, маршрутизация ответа
// установить путь для ответов (обратный маршрутный путь)
List rrp = message.getReverseRoutingPath();
SIDestinationAddress fixup = 
   SIDestinationAddressFactory.getInstance().
      createSIDestinationAddress("FixReply", false);
// вставить перед ответом по умолчанию
rrp.add(0, fixup);
message.setReverseRoutingPath(rrp);

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

  1. Найдите дескриптор развертывания для EJB-проекта и откройте его.
  2. В нижней части редактора дескриптора развертывания выберите закладку Mediation Handlers.
  3. Вы увидите пустой список. Нажмите Add.
  4. В диалоговом окне Define Mediation Handler введите атрибуты обработчика-посредника. Для нашего сценария важны два: Name и Handler класс. Имя может любым, но хорошей является идея использовать имя класса обработчика-посредника (без пакета). Имя класса должно быть указано полностью. Нажмите Browse и введите первые несколько символов имени класса обработчика, затем выберите его из списка.
  5. Нажмите Finish.
    Рисунок 12. Определение обработчика-посредника в дескрипторе развертывания
    Рисунок 12. Определение обработчика-посредника в дескрипторе развертывания
    Рисунок 12. Определение обработчика-посредника в дескрипторе развертывания

    Редактор дескриптора развертывания автоматически помещает обработчик-посредник в список с тем же именем; результат показан на рисунке 13. Вы можете поместить обработчик и в другие списки, но для нашего сценария это не нужно.


    Рисунок 13. Развернутый обработчик-посредник
    Рисунок 13. Развернутый обработчик-посредник
    Рисунок 13. Развернутый обработчик-посредник
  6. Сохраните дескриптор развертывания. Проверьте, что EAR, содержащий обработчик-посредник EJB-проекта, развернут на вашем сервере.

Теперь нам необходимо сообщить шине о списке обработчиков-посредников. Для этого выполните следующие действия:

  1. Выберите Service integration => Buses => TypeMap => Mediations => New.
  2. Введите имя в поле Mediation name. Можно ввести имя, совпадающее с именем списка обработчиков.
  3. В поле Handler list name введите имя списка обработчиков-посредников, указанное в развернутом EAR.
  4. Нажмите OK.
    Рисунок 14. Определение объекта-посредника
    Рисунок 14. Определение объекта-посредника

Затем нам необходимо установить объект-посредник для адресата входящей/исходящей службы. Для этого:

  1. Выберите Service integration => Buses => TypeMap => Destinations.
  2. В диалоговом окне "Destinations" выберите адресат исходящей службы, имеющей тип Web-служба, и нажмите Mediate.
    Рисунок 15. Адресаты
    Рисунок 15. Адресаты
    Рисунок 15. Адресаты
  3. В диалоговом окне "Mediate Destinations" вы можете выбрать объект-посредник для адресата. Поскольку есть только один, определенный на данный момент времени, он уже выбран. Нажмите Next.
    Рисунок 16. Выбор объекта-посредника, шаг 1
    Рисунок 16. Выбор объекта-посредника, шаг 1
    Рисунок 16. Выбор объекта-посредника, шаг 1
  4. В следующем диалоговом окне выберите узел, где будет работать объект-посредник. В нашем случае есть только один, поэтому просто нажмите Next.
  5. В последнем диалоговом окне подтвердите изменения, сделанные вами в предыдущих диалоговых окнах. Нажмите Finish.
  6. Отобразится диалоговое окно, изображенное на рисунке 17, в котором подтверждается, что адресат исходящей службы связан с объектом-посредником P1S1Transform.
    Рисунок 17. Подтверждение создания объекта-посредника
    Рисунок 17. Подтверждение создания объекта-посредника
    Рисунок 17. Подтверждение создания объекта-посредника

Объект-посредник для адресата перехвата ответов

Нам необходимо преобразовать ответ так же, как и запрос. На самом деле мы просто заменим ответ от провайдера службы ответом, ожидаемым входящей службой. Мы можем сделать так, потому что ответы как для инициатора запроса так и для провайдера являются пустыми. Нам необходимо создать новый обработчик-посредник с именем P1VoidReply. Для нашего сценария мы создадим класс в том же EJB-проекте P1S1Transform для минимизации количества приложений, работающих на сервере приложений. Как обработчик-посредник, P1VoidReply имеет такую же общую форму, что и P1S1Transform, но нуждается только в одном свойстве контекста - traceOn. P1VoidReply должен получать доступ к ответному сообщению так же, как P1S1Transform получает доступ к сообщению запроса.

В листинге 9 приведена часть кода для P1VoidReply, которая выполняет работу по замещению сообщения. Обработчик просто замещает тело ответа строкой newBody. Наконец, новое тело сообщения должно быть сохранено в формате, подходящем для входящей службы; вы можете посмотреть на форматную строку, используемую для этого. Синтаксис форматной строки соответствует описанному выше для входящей службы.

Листинг 9. Обработчик-посредник FixReply
   try {
	String newBody = "<env:Envelope 
  xmlns:ns1=\"http://pubsubhub.ibm.com\" 
  xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">
  <soapenv:Body xmlns:xsi=
  \"http://www.w3.org/2001/XMLSchema-instance\" 
  xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
  xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" 
  xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
  <p895:publicshResponse 
  xmlns:p895=\"http://pubsubhub.ibm.com\"/>
  </soapenv:Body></env:Envelope>";
	// Заменить содержимое сообщения новым графом
	DataGraph newGraph = 
    message.getNewDataGraph(SIApiConstants.JMS_FORMAT_TEXT);
	    newGraph.getRootObject().set("data/value", newBody);
message.setDataGraph(newGraph, SIApiConstants.JMS_FORMAT_TEXT);

// получить сообщение, представленное как SOAP-сообщение 
	String format = "SOAP:inport:TypeMap:PublisherP1T1Service:SOAPHTTPChannel1InboundPort,
http://www.ibm.com/websphere/sib/webservices/flurryNode01Cell/TypeMap/Service,
PublisherP1T1Service,SOAPHTTPChannel1InboundPort";
	DataGraph soap = message.getNewDataGraph(format);
	message.setDataGraph(soap, format);
   } catch (Exception e) {
	...
   }
   return true;

Установка свойств контекста

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

Для установки свойства контекста выполните следующие действия:

  1. Выберите Service integration => Buses => TypeMap => Destinations=>destination_name=>Context Properties=>New.
  2. Введите имя, тип и значение и нажмите OK. На рисунке 18 показан результат определения свойств контекста traceOn и xsltFileName для адресата исходящей службы.
    Рисунок 18. Свойства контекста для адресата исходящей службы
    Рисунок 18. Свойства контекста для адресата исходящей службы
    Рисунок 18. Свойства контекста для адресата исходящей службы

    На рисунке 19 показан результат определения свойства контекста traceOn для адресата перехвата ответов.
    Рисунок 19. Свойства контекста для адресата перехвата ответов
    Рисунок 19. Свойства контекста для адресата перехвата ответов
    Рисунок 19. Свойства контекста для адресата перехвата ответов

Тестирование сценария шины

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

Средства разработки Rational являются прекрасным местом для тестирования. Вы можете воспользоваться всеми возможностями отладки, доступными в этих программах, таких как точки останова, контроль переменных и т.д. Вы можете создать определенного на шине клиента входящей службы с помощью программы WSDL explorer, которая является частью инструментальных средств Rational, или с помощью мастера создания клиента Web-службы, тоже являющегося частью средств Rational.

Обратиться к WSDL для входящей службы на шине можно при помощи общего синтаксиса http:// http://<server:port>/sibus/wsdl/<bus_name>/<inbound_service_name>. Для Application Server V6.0.1 или более ранних версий вы должны указывать WSDL, использовавшийся для определения входящей службы, и изменить адрес оконечной точки. Это нужно делать из-за рассмотренного раннее ограничения, относящегося к внешней схеме, которое не дает программам использовать WSDL, созданный автоматически шиной для описания входящей службы.

Еще один полезный совет при тестировании и отладке сценариев, использующих WebSphere Messaging Resources. Если вы настроите параметры трассировки сервера приложений в *=info: SIBMessageTrace=all, то увидите некоторую полезную диагностическую информацию, помещаемую в файл trace.log для профиля сервера приложений. Эта трассировочная информация особенно полезна для обнаружения неправильных форматных строк.

Заключение

В данной статье рассмотрено использование Application Server V6 Messaging Resources для создания простого ESB-сценария, который выполняет преобразование и маршрутизацию в контексте Web-служб. В статье были детально описаны установка необходимых входящих и исходящих служб, а также необходимых адресатов и обработчиков-посредников. Этот базовый пример должен помочь вам при создании более сложных ESB-сценариев. Обсуждение более сложных сценариев можно найти в книге Patterns: SOA with an Enterprise Service Bus in WebSphere Application Server V6


Ресурсы для скачивания


Похожие темы

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=WebSphere, SOA и web-сервисы
ArticleID=109167
ArticleTitle=ESB на практике
publish-date=09212005