IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  SOA и Web-сервисы | WebSphere  >

Реализация WS_Notification в WebSphere Application Server V6.1

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

developerWorks
Опции документа

Опции документа, требующие включения JavaScript, не отображаются

Обсудить

Исходные тексты примера


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: сложный

Борис Люблинский, разработчик, CNA Insurance

30.05.2007

Одним из преимуществ сервис-ориентированной архитектуры (Service-Oriented Architecture - SOA) является возможность создания композитных сервисов для реализации новых решений. В данной статье рассказывается о том, как использовать события для создания композитных сервисов, а также о поддержке WS-Notification в IBM® WebSphere® Application Server 6.1. Также будет приведен пример кода для выполнения компоновки сервисов с использованием механизма публикация/подписка.

Компоновка сервисов и события

Сервис-ориентированная архитектура (Service-Oriented Architecture - SOA) выходит на главные роли в архитектуре предприятия, становясь ведущим подходом к проектированию, реализации и внедрению корпоративных решений. Основные преимущества SOA проявляются при компоновке существующих сервисов в сложные корпоративные решения - композитные сервисы (composite services). При вызове композитный сервис взаимодействует с подчиненными ему сервисами для выполнения связанного набора бизнес-функций. Композитный сервис руководит другими используемыми им сервисами, управляя правилами, контролирующими последовательность активизации. Композиция сервисов позволяет создавать решения, выполняющиеся в тандеме с существующими сервисами. Такие решения являются быстро развертываемыми и предлагают возможности повторного использования и интегрированного доступа ко множеству существующих сервисов.

Некоторые побудительные причины создания композитных сервисов определены в статье Али Арсанджани (Ali Arsanjani) "На пути к языку шаблонов для сервис-ориентированной архитектуры и интеграции, часть 2: Композиция сервисов" (см. ссылку в разделе "Ресурсы").

  • Замещение. Когда конкретный сервис используется в нескольких составных сервисах (процессах), способность к компоновке позволяет одновременно внедрить изменения в сервисе во все бизнес-процессы, использующие этот сервис.
  • Консолидация или единая точка доступа. В этом случае композитный сервис может использоваться в качестве единой точки доступа для набора взаимосвязанных действий, таких как выполнение расчетов страхового риска или страховой премии для различных видов страхования. Использование композитного сервиса не требует от пользователя понимания отличий в вычислениях и правил, определяющих конкретный алгоритм. Пользователь активизирует один (композитный) сервис; например, вычисление политики премирования (которая, в свою очередь, зависит от входных данных) активизирует сервис вычисления премии для конкретного вида страхования.
  • Рекомпозиция или изменение контекста. В данном случае реализация композитного сервиса может оградить пользователя от изменений, вызванных появлением новых бизнес-целей. Это позволяет быстро реагировать на новые условия и динамически менять функциональность сервисов (а также взаимодействий между ними) для решения проблемы. Такие изменения обычно скрыты за интерфейсом композитного сервиса, что приводит к минимальному или слабому влиянию на потребителей сервиса.

Хотя самой популярной стратегией реализации композитных сервисов сегодня является "оркестровка" (orchestration) сервисов, все большее внимание уделяется основанной на событиях реализации композиции сервисов (см. статьи "Сервис-ориентированная композиция в BPEL4WS" и "Инструментальные средства для композитных Web-сервисов: Краткое описание", ссылки на которые приведены в разделе "Ресурсы").

Управляемая событиями архитектура

Одним из новых "умных" слов, которое становится очень популярным (едва ли не более популярным, чем SOA), является управляемая событиями архитектура (event-driven architecture - EDA). (Прочтите статью "Управляемая событиями архитектура дополняет SOA", ссылка на которую приведена в разделе "Ресурсы".) Gartner Group определяет следующие различия между SOA и EDA:

  • Основа SOA
    • Поддерживает соединения один-к-одному
    • Имеет систему маршрутизации потока данных, который управляется клиентом (отправителем)
    • Применяет линейный алгоритм выполнения через иерархию модулей
    • Закрыта для новых непредвиденных входных данных после начала работы
  • Основа EDA
    • Поддерживает соединения многие-ко-многим
    • Использует алгоритм управления потоком данных, определяемый принимающей стороной на основе самого сообщения
    • Поддерживает динамические, параллельные, асинхронные потоки данных через сеть модулей
    • Может реагировать на новые внешние входные данные, которые могут поступать в любое время

Объединение SOA и EDA приводит к еще одному архитектурному стилю (и акрониму), становящемуся сегодня популярным, - SOA 2.0. В двух словах, этот новый стиль предлагает расширить обычные взаимодействия сервисов типа точка-точка стилем взаимодействия публикация/подписка (pub/sub), показанным на рисунке 1.


Рисунок 1. Взаимодействие сервисов в стиле публикация/подписка
Рисунок 1. Взаимодействие сервисов в стиле публикация/подписка

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

В данном случае потребители сервиса публикуют события, используя механизм pub/sub (отдельная тема), который затем доставляет их потребителям сервиса (подписанным на эту тему). Механизм pub/sub, аналогично любому посреднику, обеспечивает уровень разделения потребителей и провайдеров сервисов, который позволяет очень гибко реализовать композитные сервисы. Любое количество потребителей может передавать запросы в одну и ту же тему, и любое количество провайдеров может прослушивать (через подписку) одну и ту же тему. Следовательно, взаимодействия сервисов, основанные на механизме pub/sub, могут поддерживать отношения многие-ко-многим.

Композитный сервис в данном случае может быть реализован так, как показано на рисунке 2.


Рисунок 2. Реализация композитного сервиса с использованием событий
Рисунок 2. Реализация композитного сервиса с использованием событий

Потребитель сервиса посылает инициирующее событие, которое доставляется (через механизм pub/sub) набору сервисов, которые подписаны на это событие. Каждый сервис, в свою очередь, может передать еще одно сообщение, которое будет активировать (через этот же механизм pub/sub) еще один набор сервисов. Такая последовательность событий эффективно определяет композитный сервис. Реализация композитных сервисов через механизм pub/sub обеспечивает очень гибкие решения. Изменяя набор сервисов, подписанных на конкретную тему, можно полностью изменять реализацию композитного сервиса. Того же самого можно достичь иным путем - изменить тему, в которую потребитель передает первоначальное событие.

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


Рисунок 3. Композитный сервис обработки исков
Рисунок 3. Композитный сервис обработки исков

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

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


Рисунок 4. Композитный сервис обработки исков, использующий события
Рисунок 4. Композитный сервис обработки исков, использующий события

В этой реализации роль композитного сервиса исков играет механизм pub/sub. Потребитель сервиса в этом случае публикует запрос на обработку исков в предопределенной теме, которую прослушивают все системы обработки исков. (Другим вариантом является фильтрация запроса в конкретную систему обработки исков на основе его содержимого. Такой вариант поддерживается большинством реализаций pub/sub.) Каждая система обработки исков проверяет, может ли она обработать конкретный запрос, и если может, то обрабатывает его и посылает ответ потребителю. Внедрение дополнительной системы в данном случае не требует каких-либо изменений существующих реализаций. Единственным требованием для новой системы является распознавание запросов при их появлении и их обработка.

Спецификации WS-Notification

До недавнего времени большинство реализаций pub/sub было ограничено ориентированным на сообщения программным обеспечением промежуточного уровня (message-oriented middleware). Такие продукты, как WebSphere MQ и Microsoft® MSMQ, были непосредственно объединены с механизмами pub/sub. Развивающиеся брокеры сообщений, интегрированные в корпоративные приложения, и реализации Java™ Message Service (JMS) в серверах J2EE-приложений (Java 2 Platform, Enterprise Edition) тоже предоставляли реализации pub/sub. К сожалению, возможность взаимодействия между ними довольно низка - каждая предоставляет собственную реализацию и требует поддержки конкретным поставщиком. С развитием Web-сервисов важность парадигмы pub/sub снова возросла.

Поэтому комитетом OASIS Web-services Notification Technical committee были выпущены три следующие взаимосвязанные спецификации (ссылки на более подробную информацию приведены в разделе "Ресурсы"):

  • WS-BaseNotification. Определяет два общих интерфейса, используемых в сценариях обычных механизмов pub/sub, источник уведомлений (notification producer) и менеджер подписки (subscription manager). Спецификация определяет роли обоих интерфейсов и методы, которые должны поддерживаться каждым из них.
  • WS-BrokeredNotification. Определяет брокера уведомлений, который может использоваться в качестве посредника между провайдерами и потребителями уведомлений (см. рисунок 1). Спецификация определяет интерфейс брокера уведомлений вместе с методами, которые должен предоставлять этот интерфейс. Она также определяет интерфейс регистрации издателя (publisher), позволяющий ему явно регистрировать себя в брокере.
  • WS-Topics. Определяет правила указания и обращения к опубликованным темам.

Хороший обзор спецификаций и их совместного использования представлен в статье "События и сервис-ориентированная архитектура: Спецификации OASIS Web-services Notification" (ссылки на дополнительную информацию приведены в разделе "Ресурсы").

Этот набор спецификаций принес реализацию pub/sub в мир Web-сервисов. Он позволяет каждому создавать основанные на стандартах и Web-сервисах реализации механизмов pub/sub.

Реализация WS-Notification в WebSphere Application Server 6.1

Одной из новых реализаций механизма pub/sub является реализация WS-Notification в WebSphere Application Server (Application Server) 6.1. В оставшейся части статьи мы будем рассматривать эту реализацию.

Хотя реализация WebSphere Application Server 6.1 поддерживает все три перечисленные выше спецификации, в данной статье внимание концентрируется на брокере уведомлений, реализованном как часть системы обмена сообщениями платформы Application Server. (Отличное объяснение реализации Application Server вместе с набором архитектурных схем, демонстрирующих ее работу, предоставляется в IBM Redbook "Руководство по Web-сервисам для WebSphere Application Server Version 6.1". Ссылка приведена в разделе "Ресурсы".)

Настройка ресурсов для WS-Notification

Реализация сервиса WS-Notification зависит от Services Data Object (SDO) Repository (приложение, поставляемое IBM), которое должно быть установлено до настройки WS-Notification.

Настройка WebSphere Application Server 6.1 для использования WS-Notification в простейшем случае является довольно не сложной (см. раздел "Ресурсы"). Ее можно выполнить при помощи интерфейса консоли администратора Application Server (альтернативный способ - настройка WS-Notifications из командной строки с использованием сценария wsadmin). Настройка с использованием консоли администратора требует выполнения следующих действий:

  1. Поскольку WS-Notification реализована как часть интегрированной шины сервисов (service integration bus - SIB) Application Server, прежде всего, должны быть созданы экземпляр SIB и участник SIB [существующий сервер(ы)]. Пример конфигурации шины показан на рисунке 5.

    Рисунок 5. Пример конфигурации шины
    Рисунок 5. Пример конфигурации шины

  2. После создания SIB для создания сервера WS-Notification можно использовать мастер, предоставляемый сервером приложений. Мастер может быть вызван из панели шины путем нажатия ссылки WS-Notification services и добавления нового сервиса уведомлений. В мастере выполните следующие действия:
    1. Определите имя сервиса WS-Notification (оно влияет только на установку части URL, по которому сервис будет доступен) и SIB (из ниспадающего меню), на которой размещены ресурсы системы обмена сообщениями. Здесь нужно выбрать SIB, созданную на предыдущем шаге.
    2. Укажите список обработчиков jax-rpc и настройки системы защиты для сервиса уведомлений (в нашем примере мы не используем ни одну из этих настроек).
    3. Создайте или выберите точки сервиса (service points) для WS-Notification. Если точки сервиса уже определены, можно выбрать одну из них. В противном случае вы должны создать новую точку. В нашем случае мы начинаем с нового экземпляра шины, поэтому должны создать точку сервиса. Создание точки сервиса выполняется за два шага. Во-первых, выберите участника SIB (из ниспадающего меню) и дайте ему имя. Во-вторых, выберите или создайте прослушиватель уведомлений. В нашем случае мы создаем новый прослушиватель. Для этого необходимо указать имя прослушивателя, тип связывания (SOAP over HTTP в нашем случае) и корневой URL (мы используем по умолчанию), а также порт сервиса Web-services Description Language (мы используем по умолчанию).
    4. Настройте постоянное пространство имен темы WS-Notification. Мы не используем его в нашем примере.
    5. Просмотрите предоставленные значения.

После завершения мастером настройки WS-Notification вы увидите окно, аналогичное показанному на рисунке 6.


Рисунок 6. Настройка сервиса WS-Notification
Рисунок 6. Настройка сервиса WS-Notification

Как можно увидеть на рисунке 6, сервис WS-Notification состоит из трех реальных сервисов:

  • Notification Broker (брокер уведомлений) - это основной компонент этой реализации. Он служит точкой раздела между приложениями, генерирующими события и потребляющими их. Application Server предоставляет реализацию брокера, поддерживающую всю необходимую функциональность, включая обслуживание списков активных подписчиков, анализ и сверку тем подписки, распределение уведомлений подписчикам и обработку некоторых аспектов цикла жизни подписки. Этот интерфейс определен спецификацией WS-BrokeredNotification.
  • Subscription Manager (менеджер подписки) поддерживает функциональность, необходимую для управления циклом жизни подписок потребителей. Он определяет обмен сообщениями для управления ресурсами подписки. Имеется два стиля интерфейса Subscription Manager: основной (base) и останавливаемый (pausable). Оба стиля реализованы в WebSphere Application Server 6.1. Этот интерфейс определен спецификацией WS-BaseNotification.
  • Publisher Registration (регистрация издателя) поддерживает функциональность, необходимую для управления циклом жизни регистрации издателя. Этот интерфейс определен спецификацией WS-BrokeredNotification.

В наших примерах кода мы используем только сервисы Notification Broker и Subscription Manager. Для доступа к этим сервисам нам понадобятся WSDL-файлы (Web-сервис Definition Language) для них. К счастью, консоль администратора сервера приложений поддерживает создание следующих четырех WSDL-файлов для каждого создаваемого сервиса:

  • xxxPortTypes.wsdl содержит определение порта данного сервиса.
  • xxxBinding.wsdl зависит от xxxPortTypes.wsdl и содержит определение связывания для данного порта.
  • xxxService.wsdl зависит от xxxBinding.wsdl и содержит определение сервиса для данного связывания (с корректным URL-адресом сервиса, каким он был создан).
  • xxx NonBound.wsdl зависит от xxxPortTypes.wsdl и содержит определения связывания и сервиса для данного порта (с общим URL-адресом сервиса).

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

(Примечание: Технически WSDL-файлы, создаваемые сервером WebSphere Application Server, могут непосредственно использоваться для генерирования кода (см. далее), но только если вы подключены к интернету. Файлы используют полные имена машины и URL-адреса импортированных файлов. Если, подобно мне, вы хотите писать код в автономном режиме, например, в поезде или поздно ночью в комнате перед телевизором, они работать не будут. Для работы с примерами в автономном режиме я заменил определения адреса оконечной точки в файлах xxxService.wsdl на локальную хост-машину вместо полного имени машины. Я также поместил все необходимые файлы в WSDL-проект и изменил параметры расположения в файлах так, чтобы они указывали на локальные версии.)

Реализация потребителя WS-Notification

Реализация потребителя состоит из трех основных частей:

  • Потребитель уведомлений (notification consumer)
  • Подписчик на уведомления (notification subscriber)
  • Компонент для отмены подписки (notification unsubscriber) (не обязателен)

В следующих разделах описывается каждый из них.

Потребитель уведомлений

Потребитель уведомлений представляет собой Web-сервис, получающий уведомления (публикации). Этот сервис должен реализовывать метод notify, определенный спецификацией WS-BaseNotification. WSDL-файл для него может быть создан на базе WSDL-файла, предоставляемого группой OASIS (для определения функциональности WS-BaseNotification) и приведенного в листинге 1 (этот файл также предоставляется с кодом, сопровождающим данную статью).


Листинг 1. WSDL-файл для потребителя уведомлений
                
<?xml version="1.0" encoding="UTF-8">
<wsdl:definitions targetNamespace="com.cna.wsn/consumer" 
    xmlns="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:wsn-bw="http://docs.oasis-open.org/wsn/bw-2" 
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:tns="com.cna.wsn/consumer" 
    xmlns:wsdl1="http://docs.oasis-open.org/wsn/bw-2">
    <wsdl:import namespace="http://docs.oasis-open.org/wsn/bw-2" location="bw-2.wsdl" />
    <wsdl:binding name="NotificationConsumerBinding" type="wsn-bw:NotificationConsumer">
        <wsdlsoap:binding style="document" 
                                    transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="Notify">
            <wsdlsoap:operation soapAction="" />
            <wsdl:input>
                <wsdlsoap:body use="literal" />
            </wsdl:input>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="NotificationConsumerService">
        <wsdl:port name="NotificationConsumerPort" 
                                          binding="tns:NotificationConsumerBinding">
            <wsdlsoap:address location="http://myserver.cna.com/Consumer" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>
      

Данный WSDL-файл можно использовать для генерирования провайдера стандартных Web-сервисов (скелет JavaBeans-компонентов). Очень простая реализация потребителя уведомлений (опущен повторяющийся код) представлена в листинге 2.

(Примечание: Я использовал WebSphere Application Server Toolkit (AST) 6.1, чтобы сгенерировать код для данной статьи.)


Листинг 2. Реализация потребителя уведомлений
                
package wsn.consumer.cna.com;

import java.util.Iterator;
import javax.xml.rpc.server.ServletEndpointContext;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPFactory;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.oasis_open.docs.wsn.bw_2.NotificationConsumer;
import com.ibm.websphere.sib.wsn.NotificationMessage;
import com.ibm.websphere.sib.wsn.TopicExpression;
import com.ibm.websphere.wsaddressing.EndpointReference;

public class NotificationConsumerBindingImpl implements NotificationConsumer{

    public void notify(NotificationMessage[] notificationMessage, SOAPElement[] any) 
                                                     throws java.rmi.RemoteException {
	
        System.out.println("Receiving Notification ...");
				
        // Обработать каждое NotificationMessage
        for (int i=0; i<notificationMessage.length; i++) {                
            NotificationMessage message = notificationMessage[i];
                
            // Получить содержимое сообщения 
            SOAPElement messageContent = message.getMessageContents();
            System.out.println("Notification message:");
            try{
                OutputFormat ouf = new OutputFormat();
                XMLSerializer serializer = new XMLSerializer(System.out,ouf);
                serializer.serialize(messageContent);
                System.out.println(" ");
            }
            catch(Exception e){
                e.printStackTrace();
            }
    		
            // Получить выражение, указывающее на то, с какой темой связано сообщение 
            TopicExpression topic = message.getTopic();
            System.out.println("Notification topic:");
            System.out.println(topic.getTopic());
                
            // Получить ссылку на источник (он не обязателен, поэтому ссылка может
            // быть равна null)
            EndpointReference producerRef = message.getProducerReference();
            if(producerRef != null){
                System.out.println("Producer Reference: " + producerRef);
                try{
                    com.ibm.wsspi.wsaddressing.EndpointReference ref = 
                          (com.ibm.wsspi.wsaddressing.EndpointReference)producerRef;
                    SOAPFactory sFactory = SOAPFactory.newInstance();
                    SOAPElement sel = sFactory.createElement("root");
                    SOAPElement root = ref.getSOAPElement(sel);
                    ...................................
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            }
                
            // Получить ссылку на подписку (она не обязательна, поэтому ссылка может
            // быть равна null)
            EndpointReference subscriptionRef = message.getSubscriptionReference();
            if(subscriptionRef != null){
                System.out.println("Subscription Reference: " + subscriptionRef);
                .............................................
            }
        }
        
        // Получить информацию, связанную с сообщением 
        if(any != null){
            for(int i=0; i < any.length; i++){
                SOAPElement extra = any[i];
                if(extra != null){
                    System.out.println("Additional information:");
					
                .........................................
                }
            }
         }    
     }
}
      

Полная версия класса, приведенного в листинге 2, находится в примере кода, сопровождающего данную статью.

Как определено в WSDL-файле потребителя уведомлений сервиса, метод notify принимает два параметра: массив уведомлений и массив дополнительной информации. Реализация, приведенная в листинге 2, просто выводит содержимое обоих массивов на экран. Каждое уведомление имеет следующие четыре части:

  • Бизнес-сообщение. Сообщение, представляемое издателем уведомления.
  • Тема. Тема, с которой связано бизнес-сообщение.
  • Ссылка на подписку. Ссылка, определяющая данную подписку в брокере сообщений.
  • Ссылка на источник. Ссылка, определяющая источник, передающий сообщение; предоставляется только если источник явно зарегистрирован в брокере.

Как определено в спецификации Web-services Base Notification 1.3. (см. раздел "Ресурсы"), дополнительная информация предоставляет приспосабливающие элементы открытого содержимого (open content accommodating elements), которые могут понадобиться расширениям, созданным на базе BaseNotification, включая элементы для механизмов дополнительной фильтрации. В настоящее время этот массив не используется нашей реализацией.

В нашей реализации WS-Notification бизнес-сообщение и дополнительная информация представлены как SOAPElements. Для вывода (и обработки) SOAPElement я использую новую версию JAX-RPC, поддерживаемую сервером WebSphere Application Server 6.1, в которой SOAPElement наследуется из XML-узла. Узел может быть непосредственно выведен на экран благодаря поддержке, имеющейся в Apache XERCES. Обе ссылки реализованы в виде класса EndpointReference. Класс EndpointReference не понятен и не предоставляет каких-либо методов для их обработки. К счастью, в реализации WebSphere Application Server 6.1 этот класс наследуется из специфического для IBM класса com.ibm.wsspi.wsaddressing.EndpointReference, который предоставляет метод, преобразующий его содержимое в SOAPElement. Я использую этот SOAPElement, представляющий содержимое EndpointReference для обработки и вывода ссылок.

Подписчик на уведомления

Для того чтобы потребитель уведомлений начал принимать уведомления, брокер должен знать о нем. Подписка - это процесс обеспечения брокера информацией о потребителе (его адрес оконечной точки и тема, которой он интересуется). Интерфейсом NotificationBroker поддерживается метод subscription. Это означает, что подписчик на уведомления может быть реализован как потребитель сервиса NotificationBroker. Я создал такого потребителя на основе WSDL-файла NotificationBroker (инструкции по загрузке WSDL-файла с сервера приложений приведены в разделе "Настройка ресурсов для WS-Notification").

Очень простая реализация подписчика на уведомления (опущен повторяющийся код) приведена в листинге 3.


Листинг 3. Реализация подписчика на уведомления
                
package com.cna.notification.tester;

import java.net.URI;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import javax.xml.rpc.holders.CalendarHolder;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPFactory;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.oasis_open.docs.wsn.b_2.holders.AnyArrayHolder;
import org.w3.www.holders.EndpointReferenceTypeHolder;
import com.cna.services.container.epContainer;
import com.ibm.www.NotificationBroker;
import com.ibm.www.NotificationBrokerProxy;
import com.ibm.websphere.sib.wsn.AbsoluteOrRelativeTime;
import com.ibm.websphere.sib.wsn.Filter;
import com.ibm.websphere.sib.wsn.TopicExpression;
import com.ibm.wsspi.wsaddressing.EndpointReference;
import com.ibm.wsspi.wsaddressing.EndpointReferenceManager;


public class subscriberBasic {
	
    private NotificationBroker stub = null;
	
    public subscriberBasic(){

       try{

//          Получить заглушку (stub) для порта, на котором вы хотите активировать операции
            NotificationBrokerProxy proxy = new NotificationBrokerProxy();
            stub = proxy.getNotificationBroker();
			
        }
        catch(Exception e){
            System.out.println("Error creating service stub");
            e.printStackTrace();
        }
    }

    public void subscribe(String ep){
		
        if(stub == null)
            return;
		
//      Создать ConsumerReference. 
//      Содержит адрес потребителя Web-сервиса, который подписывается
		EndpointReference consumerEPR = null;
        try{
            URI epURI = new URI(ep);
            consumerEPR = EndpointReferenceManager.createEndpointReference(epURI);			
        }
        catch(Exception e){
            System.out.println("Error creating notification URI");
            e.printStackTrace();
        }
        if(consumerEPR == null)
            return;
		
//      Создать Filter. 
//      Предоставляет имя темы, на которую вы хотите подписать потребителя
        Filter filter = new Filter();

//      Создать выражение темы и добавить его к фильтру. prefixMappings - это 
//      отображения между префиксами пространства имен и 
//      соответствующими им пространствами имен для префиксов, 
//      использованных в выражении
        Map prefixMappings = new HashMap();
        prefixMappings.put("test", "http://www.cna.com/test/topic");
        TopicExpression exp = 
		      new TopicExpression(TopicExpression.SIMPLE_TOPIC_EXPRESSION, 
		                                      "test:TestTopic", prefixMappings);        
        filter.addTopicExpression(exp);
        
//      Создать InitialTerminationTime. 
//      Это время, когда подписка будет прекращена.  
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.YEAR, 1);
        AbsoluteOrRelativeTime initialTerminationTime = new AbsoluteOrRelativeTime(cal);
		        
//      Создать информацию Policy 
        SOAPElement[] policyElements = null;
		        
//      Создать контейнеры для хранения нескольких значений, возвращаемых брокером:
//      Ссылка на подписку
        EndpointReferenceTypeHolder subscriptionRefHolder = 
		                                         new EndpointReferenceTypeHolder();
		        
//      Текущее время у брокера
        CalendarHolder currentTimeHolder = new CalendarHolder();
		        
//      Время завершения подписки
        CalendarHolder terminationTimeHolder = new CalendarHolder();
		        
//      Любые дополнительные элементы
        AnyArrayHolder anyOtherElements = new AnyArrayHolder();

        try{
//          Активизировать операцию Subscribe, вызывая соответствующий метод заглушки
            System.out.println("Sending message to the Broker... ");
            stub.subscribe(consumerEPR,
		                       filter,
		                       initialTerminationTime,
		                       policyElements,
		                       anyOtherElements,
		                       subscriptionRefHolder,
		                       currentTimeHolder,
		                       terminationTimeHolder);
		        
//          Получить возвращаемые значения:
//          Ссылка на оконечную точку для подписки необходима для последующего 
//          управления циклом жизни подписки, например, приостановкой или прекращением
//          подписки  
            com.ibm.websphere.wsaddressing.EndpointReference subscriptionRef = 
			                                            subscriptionRefHolder.value;
            System.out.println("Subscription Reference: " + subscriptionRef);
            epContainer.saveReference(subscriptionRef,0);
	..............................................        	
//         Текущее время у брокера
            Calendar currentTime = currentTimeHolder.value;
            System.out.println("Current Broker time: " + currentTime.toString());
		        
//          Время завершения подписки
            Calendar terminationTime = terminationTimeHolder.value;
            System.out.println("Subscription termination time: " + 
                                                    terminationTime.toString());
		        
//          Любая другая информация
            SOAPElement[] otherElements = anyOtherElements.value;
            if(otherElements != null){
                  ........................................
            }
        }
        catch(Exception e){
            System.out.println("Error invoking notification broker");
            e.printStackTrace();
        }
    }
}
     

Полную версию класса, приведенного в листинге 3, можно получить, загрузив код, сопровождающий данную статью.

В данной реализации класс subscriber имеет два метода - простой конструктор, создающий локальную заглушку, которая используется для взаимодействия с Notification Broker, и метод subscribe, активизирующий метод subscription брокера.

Как определено в спецификации Web-services Base Notification 1.3. (см. раздел "Ресурсы"), метод subscribe предоставляет следующие параметры в Notification Broker:

  • Элемент ссылки на оконечную точку потребителя (Consumer endpoint reference element) принимает форму EndpointReference. Эта ссылка может быть создана на базе URL, который прослушивает потребитель для уведомлений, используя класс EndpointReferenceManager, предоставленный реализацией WebSphere Application Server.
  • Фильтр подписки (Subscription filter) позволяет подписчику определить список тем, которыми интересуется потребитель уведомлений. Этот список определяется как список расширений тем, присоединенных к фильтру. Выражение темы определяется в терминах типа выражения, полного имени и отображения префикса. Имя префикса темы - это карта, определяющая набор префиксов и URL-адресов, назначенных этим префиксам.
  • Время прекращения подписки (Subscription termination time) содержит предложение инициатора запроса по начальному времени прекращения подписки. Если это время установлено в null, подписка считается вечной.
  • Элементы политики (Policy elements) - это набор открытых компонентов, использующиеся специфичным для приложения способом с целью указать относящиеся к политике требования/утверждения, связанные с запросами на подписку. Не совсем понятно, как этот параметр используется реализацией WebSphere Application Server.

Этот метод возвращает следующие параметры:

  • Ссылка на оконечную точку подписки (Subscription endpoint reference) содержит значение, которое определяет подписку в брокере уведомлений. Это значение используется для управления циклом жизни подписки, например, приостановкой или отменой подписки (см. раздел "Компонент отмены подписки"). Приведенный в листинге 3 код выводит это значение и сохраняет его для дальнейшего использования компонентом отмены подписки.
  • Текущее время и время прекращения подписки (Current and subscription termination times) определяются в брокере на основе текущего запроса подписки. Приведенный в листинге 3 пример кода просто выводит на экран эти два значения.
  • Дополнительные параметры (Additional parameters) - это механизм расширения для дальнейших улучшений реализации.

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

Компонент отмены подписки

Чтобы остановить получение уведомлений, потребитель подписки должен явно отписаться от этой подписки. Интерфейс SubscriptionManager поддерживает метод unsubscribe. Это означает, что компонент отмены подписки может быть реализован как потребитель сервиса SubscriptionManager. Я создал этого потребителя на основе WSDL-файла SubscriptionManager (инструкции по загрузке WSDL-файла с сервера приложений приведены в разделе "Настройка ресурсов для WS-Notification").

Простая реализация компонента отмены подписки представлена в листинге 4.


Листинг 4. Реализация отмены подписки
                
package com.cna.notification.tester;

import java.net.URI;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPFactory;
import com.cna.services.container.epContainer;
import com.ibm.ws.wsaddressing.WSAConstants;
import com.ibm.websphere.wsaddressing.EndpointReference;
import com.ibm.www.SubscriptionManager;
import com.ibm.www.SubscriptionManagerProxy;

public class unsubscriber {
	
    private SubscriptionManager stub = null;
	
    public unsubscriber(){

        try{

//          Получить заглушку для порта, на котором вы хотите активизировать операции
            SubscriptionManagerProxy proxy = new SubscriptionManagerProxy();
            stub = proxy.getSubscriptionManager();
        }
        catch(Exception e){
            System.out.println("Error creating service stub");
            e.printStackTrace();
        }
    }

    public void unsubscribe(int subs){
		
        if(stub == null)
            return;
	
//      Получить subscriptionReference. 		
        EndpointReference consumerEPR = epContainer.getReference(subs);
        if(consumerEPR == null){
            System.out.println("Unable to get ID");		        
            return;
        }
		
        try{
//          Активизировать операцию Unsubscribe, вызывая соответствующий метод 
            System.out.println("Sending message to the Subscription Manger... ");
            ((javax.xml.rpc.Stub)stub)._setProperty(
            		WSAConstants.WSADDRESSING_DESTINATION_EPR, consumerEPR);
            stub.unsubscribe(null);
            System.out.println("Done unsbscribing ");		        
        }
        catch(Exception e){
            System.out.println("Error invoking notification broker");
            e.printStackTrace();
        }
    }
}
     

Как и подписчик на уведомления, данный класс содержит два метода - конструктор, создающий локальную заглушку для взаимодействия с сервисом SubscriptionManager, и метод unsubscribe, реализующий реальную операцию unsubscribe. Эта операция принимает адрес оконечной точки, возвращаемый операцией subscription, и массив параметров расширения (который я не использовал в данной реализации). Операция удаляет подписку в брокере уведомлений. Адрес оконечной точки предоставляется не как полезная нагрузка, а как адресация Web-сервисов в заголовок. Он устанавливается как свойство в заглушке сервиса SubscriptionManager и затем преобразуется в заголовок реализацией исполняющей системы Web-сервисов IBM.

Реализация WS-Notification Publisher

WS-Notification Publisher - это приложение, использующееся для публикации уведомлений. Публикация производится в определенную тему в NotificationBroker, которая, в свою очередь, направляет это уведомление всем подписчикам, интересующимся данной темой. Интерфейсом NotificationBroker поддерживается метод publish. Это означает, что подписчик на уведомления может быть реализован как потребитель сервиса NotificationBroker. Я создал этого потребителя на основе WSDL-файла NotificationBroker (инструкции по загрузке WSDL-файла с сервера приложений приведены в разделе "Настройка ресурсов для WS-Notification").

Простая реализация подписчика на уведомления (опущен повторяющийся код) приведена в листинге 5.


Листинг 5. Реализация издателя
                
 package com.cna.notification.tester;

import java.util.HashMap;
import java.util.Map;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPElement;
import com.ibm.websphere.sib.wsn.NotificationMessage;
import com.ibm.websphere.sib.wsn.TopicExpression;
import com.ibm.www.NotificationBroker;
import com.ibm.www.NotificationBrokerProxy;

public class publisher {

    private NotificationBroker stub = null;
	
    public publisher(){

        try{
//          Получить заглушку для порта, на котором вы хотите активизировать операции
            NotificationBrokerProxy proxy = new NotificationBrokerProxy();
            stub = proxy.getNotificationBroker();
        }
		catch(Exception e){
            System.out.println("Error creating service stub");
            e.printStackTrace();
        }
    }

    public void publish(){
		
        if(stub == null)
            return;
//      Создать содержимое для уведомления 
        SOAPElement messageContents = null;
        try{
            SOAPFactory soapFactory = SOAPFactory.newInstance();
            messageContents = soapFactory.createElement("MyData", 
			                            "cnaData", "http://www.cna.com/data");
            messageContents.addTextNode("Some notification data");
        }
        catch(Exception e){
            System.out.println("Error creating service stub");
            e.printStackTrace();
        }
                
//      Создать уведомление из содержимого
        NotificationMessage message = new NotificationMessage(messageContents);

//      Добавить выражение topic для уведомления  
        Map prefixMappings = new HashMap();
        prefixMappings.put("test", "http://www.cna.com/test/topic");
        TopicExpression exp = 
		      new TopicExpression(TopicExpression.SIMPLE_TOPIC_EXPRESSION, 
		                                   "test:TestTopic", prefixMappings);        
        message.setTopic(exp);

//      Активизировать операцию Notify, вызвав соответствующий метод заглушки
        try{
            System.out.println("Publishing Notification ...");
            stub.notify(new NotificationMessage[] { message }, null);
            System.out.println("Publishing successful");
        }
        catch(Exception e){
            System.out.println("Error publishing message");
            e.printStackTrace();
        }
    }
}
      

Аналогично предыдущим двум классам, уже представленным в данной статье (см. листинги 3 и 4), этот класс тоже имеет два метода: конструктор, создающий локальную заглушку для взаимодействия с сервисом NotificationBroker, и publish, реализующий фактическую операцию publish.

Как определено в спецификации Web-services Base Notification 1.3. (см. раздел "Ресурсы"), метод publish представляет следующие параметры брокеру уведомлений:

  • Массив из одного или нескольких уведомлений (содержимое уведомлений определено выше в разделе "Потребитель уведомлений").
  • Массив необязательных элементов, зарезервированный для поддерживающих элементов, которые могут понадобиться для расширений. Текущая реализация WebSphere Application Server 6.1 не использует эти элементы.

Пакетирование и выполнение примера кода

В дополнение к коду, описанному выше, реализация (см. листинги 2, 3, 4, 5) содержит еще два класса: DebugHandler и epContainer, объединенные в простой Java-проект под названием DebugHandler. DebugHandler - это простой JAX-RPC-обработчик, использующийся для вывода SOAP-сообщения либо потребителем, либо провайдером сервиса. Код отладочного обработчика приведен в листинге 6.


Листинг 6. Код DebugHandler
                
package com.cna.service.handler;

import javax.xml.namespace.QName;
import javax.xml.rpc.handler.GenericHandler;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.SOAPMessage;

public class debugHandler extends GenericHandler {

    public QName[] getHeaders() {
         return null;
    }

    public boolean handleRequest(MessageContext mc){
		
        System.out.println("Handling message request");
        printMessage(mc);
        System.out.println("Done handling message request");
        return true;
    }

    public boolean handleResponse(MessageContext mc){

        System.out.println("Handling message response");
        printMessage(mc);
        System.out.println("Done handling message response");
        return true;
	}
	
    private void printMessage(MessageContext mc){
		
        try{
            SOAPMessageContext smc = (SOAPMessageContext) mc;
            SOAPMessage sMessage = smc.getMessage();
            sMessage.writeTo(System.out);
            sMessage.saveChanges();
        }
        catch(Exception e){
            System.out.println("Error printing SOAP message");
            e.printStackTrace();
        }
    }
}
      

Этот обработчик реализует два public-метода: handleRequest и handleResponse. Эти методы активизируются исполняющей системой WebSphere Application Server Web-services как часть канала активизации сервиса (на обеих сторонах - потребителе и провайдере) Каждый из этих методов получает SOAP-сообщение (запрос и ответ) и выводит их на консоль (вместо использования DebugHandler есть возможность использовать TCP Monitor для перехвата содержимого SOAP-сообщений).

epContainer - это простой класс, используемый для передачи ссылки на подписку между реализациями подписчика и компонента отмены подписки (см. листинги 3 и 4). Реализация epContainerclass представлена в листинге 7.


Листинг 7. Реализация epContainer
                
package com.cna.services.container;

import com.ibm.websphere.wsaddressing.EndpointReference;

public class epContainer {
		
    private static EndpointReference[] references = new EndpointReference[10];
	
    public static void saveReference(EndpointReference subscriptionRef, int id){
		
        references[id] = subscriptionRef;
    }

    public static EndpointReference getReference(int id){
		
        return references[id];
    }
}
      

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

Наш простой пример представляет собой четыре динамических Web-проекта: notificationProducer, notificationConsumer, notificationSubscriber и notificationUnSubscriber. Каждый из этих проектов ссылается на DebugHandlers. Эта ссылка должна быть определена в двух местах - в project properties/Java build path и в манифесте проекта.

Хотя notificationConsumer реализует Web-сервис уведомлений, активизирующийся брокером уведомлений WebSphere Application Server, остальной код, представленный в статье, должен вызываться явно. Я создал набор JSP-страниц (JavaServer Pages) для активизации классов (листинги с 3 по 5) - по одной на класс. Пример такой JSP-страницы (для активизации notificationProducer) приведен в листинге 8.


Листинг 8. JSP-страница для активизации notificationPublisher
                
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@ page 
language="java"
%>
<jsp:useBean id="publisher" scope="session" 
                             class="com.cna.notification.tester.publisher" />
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>WSN Publisher</title>
</head>
<body>
<P>Publishing to WSN ... </P>
<% publisher.publish(); %>
</body>
</html>
      

Аналогичные JSP-страницы используются для активизации классов notificationSubscriber и notificationUnSubscriber.

Web-проекты организованы в двух корпоративных приложениях - notificationProducerEAR, содержащем первый Web-проект (то есть, notificationProducer), и notificationConsumerEAR, содержащем три остальных Web-проекта.

Простейшим тестом созданного нами программного обеспечения является запуск всего разработанного кода в следующей последовательности:

  • Подписка в брокере
  • Публикация уведомления
  • Отмена подписки

Выполнение каждого шага описывается ниже.

Подписка в брокере

Подписка нашего потребителя уведомлений в брокере производится путем выполнения notificationSubscriber JSP. При активизации этой JSP-страницы SOAP-сообщение, приведенное в листинге 9, передается брокеру.


Листинг 9. Сообщение запроса подписки
                
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header />
    <soapenv:Body>
        <p38:Subscribe xmlns:p38="http://docs.oasis-open.org/wsn/b-2">
           <ConsumerReference xmlns="http://docs.oasis-open.org/wsn/b-2">
                <wsa:Address xmlns:wsa="http://www.w3.org/2005/08/addressing">
          http://localhost:9080/notificationConsumer/services/NotificationConsumerPort
                </wsa:Address>
            </ConsumerReference>
            <Filter xmlns="http://docs.oasis-open.org/wsn/b-2">
                <b2:TopicExpression
                    Dialect="http://docs.oasis-open.org/wsn/t-1/TopicExpression/Simple"
                    xmlns:test="http://www.cna.com/test/topic"
                    xmlns:b2="http://docs.oasis-open.org/wsn/b-2">
                    test:TestTopic
                </b2:TopicExpression>
            </Filter>
            <InitialTerminationTime xmlns="http://docs.oasis-open.org/wsn/b-2">
                2007-09-11T13:07:18.332Z
            </InitialTerminationTime>
        </p38:Subscribe>
    </soapenv:Body>
</soapenv:Envelope>
	  

При получении этого сообщения брокер настраивает подписку и передает ответ, содержащий ссылку на подписку (полная информация о подписке). Мы будем использовать эту ссылку на подписку позже для отмены подписки. SOAP-сообщение, представляющее ответ брокера по настройке подписки, приведено в листинге 10.


Листинг 10. Сообщение ответа о подписке
                
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:wsa="http://www.w3.org/2005/08/addressing">
    <soapenv:Header />
    <soapenv:Body>
        <p38:SubscribeResponse
            xmlns:p38="http://docs.oasis-open.org/wsn/b-2">
            <SubscriptionReference
                xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns="http://docs.oasis-open.org/wsn/b-2"
                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:wsa="http://www.w3.org/2005/08/addressing"
                xmlns:p38="http://docs.oasis-open.org/wsn/b-2">
                <wsa:Address xmlns:wsa="http://www.w3.org/2005/08/addressing">
        http://MXPCHWKAJXS.cna.com:9080/NotificationListener/soaphttpengine
        /myBus%2FMyPubSubSubscriptionManager%2FWSNServicePointSubscriptionManagerPort
                </wsa:Address>
                <wsa:ReferenceParameters
                    xmlns:wsa="http://www.w3.org/2005/08/addressing">
                    <wsaucf:RoutingInformation
                        xmlns:wsaucf="http://ucf.wsaddressing.ws.ibm.com">
                        <wsaucf:HAClusterId>
                            00000000020004747970650010575341465f5349425f4d455f
                            55554944000475756964001036383741343237323035383844423638
                        </wsaucf:HAClusterId>
                    </wsaucf:RoutingInformation>
                    <wsaucf:VirtualHostName
                        xmlns:wsaucf="http://ucf.wsaddressing.ws.ibm.com">
                        default_host
                    </wsaucf:VirtualHostName>
                    <subscriptionId
                        xmlns="http://www.ibm.com/websphere/wsn/reference">
                        sub_8bb445adce37a29598f6d3e2_10d9cfd2503_3
                    </subscriptionId>
                    <messagingEngineUuid
                        xmlns="http://www.ibm.com/websphere/wsn/reference">
                        687A42720588DB68
                    </messagingEngineUuid>
                </wsa:ReferenceParameters>
            </SubscriptionReference>
            <p38:CurrentTime>2006-09-11T13:07:18.403Z</p38:CurrentTime>
            <p38:TerminationTime>
                2007-09-11T13:07:18.332Z
            </p38:TerminationTime>
        </p38:SubscribeResponse>
    </soapenv:Body>
</soapenv:Envelope>
	  

Кроме того, подписка может быть проверена в консоли администратора WAS, как показано на рисунке 7.


Рисунок 7. Подписка на уведомления в консоли администратора
Рисунок 7. Подписка на уведомления в консоли администратора

Публикация и получение уведомлений

Публикация сообщения в брокере выполняется путем активизации notificationProducer JSP. При его активизации в брокер передается SOAP-сообщение, приведенное в листинге 11.


Листинг 11. Публикуемое сообщение
                
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header />
    <soapenv:Body>
        <p38:Notify xmlns:p38="http://docs.oasis-open.org/wsn/b-2">
            <NotificationMessage xmlns="http://docs.oasis-open.org/wsn/b-2">
                <b2:Topic
                    Dialect="http://docs.oasis-open.org/wsn/t-1/TopicExpression/Simple"
                    xmlns:test="http://www.cna.com/test/topic"
                    xmlns:b2="http://docs.oasis-open.org/wsn/b-2">
                    test:TestTopic
                </b2:Topic>
                <b2:Message xmlns:b2="http://docs.oasis-open.org/wsn/b-2">
                    <cnaData:MyData xmlns:cnaData="http://www.cna.com/data">
                        Some notification data
                    </cnaData:MyData>
                </b2:Message>
            </NotificationMessage>
        </p38:Notify>
    </soapenv:Body>
</soapenv:Envelope>
	  

Когда брокер получает это сообщение, он проверяет подписки на тему, в которой было опубликовано сообщение, и передает уведомление, приведенное в листинге 12, всем активным подписчикам.


Листинг 12. Уведомление
                
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:wsa="http://www.w3.org/2005/08/addressing">
    <soapenv:Header/>
    <soapenv:Body>
        <p38:Notify xmlns:p38="http://docs.oasis-open.org/wsn/b-2">
            <NotificationMessage xmlns="http://docs.oasis-open.org/wsn/b-2">
                <b2:SubscriptionReference
                    xmlns:b2="http://docs.oasis-open.org/wsn/b-2">
                        <wsa:Address 
                        xmlns:wsa="http://www.w3.org/2005/08/addressing">
    http://MXPCHWKAJXS.cna.com:9080/NotificationListener/soaphttpengine/
    myBus%2FMyPubSubSubscriptionManager%2FWSNServicePointSubscriptionManagerPort
                    </wsa:Address>
                    <wsa:ReferenceParameters
                        xmlns:wsa="http://www.w3.org/2005/08/addressing">
                        <wsaucf:RoutingInformation
                            xmlns:wsaucf="http://ucf.wsaddressing.ws.ibm.com">
                            <wsaucf:HAClusterId>
                                00000000020004747970650010575341465f5349425f4d455f555549
                                44000475756964001036383741343237323035383844423638
                            </wsaucf:HAClusterId>
                        </wsaucf:RoutingInformation>
                        <wsaucf:VirtualHostName
                            xmlns:wsaucf="http://ucf.wsaddressing.ws.ibm.com">
                            default_host
                        </wsaucf:VirtualHostName>
                        <subscriptionId
                            xmlns="http://www.ibm.com/websphere/wsn/reference">
                            sub_8bb445adce37a29598f6d3e2_10d9cfd2503_3
                        </subscriptionId>
                        <messagingEngineUuid
                            xmlns="http://www.ibm.com/websphere/wsn/reference">
                            687A42720588DB68
                        </messagingEngineUuid>
                    </wsa:ReferenceParameters>
                </b2:SubscriptionReference>
                <b2:Topic
                    Dialect="http://docs.oasis-open.org/wsn/t-1/TopicExpression/Simple"
                    xmlns:wsntopprefix="http://www.cna.com/test/topic"
                    xmlns:b2="http://docs.oasis-open.org/wsn/b-2">
                    wsntopprefix:TestTopic
                </b2:Topic>
                <b2:Message
                    xmlns:b2="http://docs.oasis-open.org/wsn/b-2">
                    <cnaData:MyData
                        xmlns:cnaData="http://www.cna.com/data">
                        Some notification data
                    </cnaData:MyData>
                </b2:Message>
            </NotificationMessage>
        </p38:Notify>
    </soapenv:Body>
</soapenv:Envelope>
	  

Отмена подписки в брокере

Наконец, как упоминалось ранее, потребитель уведомлений должен явно отписаться в брокере. В противном случае он будет продолжать получать уведомления до истечения срока подписки. JSP-страница notificationUnsubscriber позволяет явно отменить подписку. При активизации этой JSP-страницы в брокер передается SOAP-сообщение, приведенное в листинге 13.


Листинг 13. Сообщение об отмене подписки
                
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header />	
    <soapenv:Body>
        <p38:Unsubscribe xmlns:p38="http://docs.oasis-open.org/wsn/b-2" />	
    </soapenv:Body>
</soapenv:Envelope>
	  

Листинг 13 является не полным. Он содержит только тело сообщения без заголовка. Причина этого заключается в том, что в реализации канала Web-сервисов WebSphere Application Server перед преобразованием свойств заглушки (см. листинг 4) в дополнительные заголовки активизируется отладочный обработчик (см. листинг 6).

Другие улучшения и варианты

Реализация спецификации WS-Notification в WebSphere Application Server 6.1 не пытается найти и, следовательно, удалить идентичные запросы на подписку. В результате, несколько подписок, даже если они одинаковы, все равно регистрируются в брокере уведомлений как разные подписки. Это означает, что одно сообщение на публикацию может вызвать получение одним и тем же потребителем нескольких уведомлений. Иногда это может быть недостатком, а в других ситуациях - очень полезной функциональностью.

Например, потребитель уведомлений может использоваться как прокси-компонент в случаях, когда реальные потребители уведомлений не могут поддерживать протокол (HTTP или JMS) или кодировку (SOAP), используемую реализацией WebSphere Application Server 6.1 (см. рисунок 8). В данном случае потребители могут использовать свои собственные (частные) каналы коммуникации для обмена данными с прокси-компонентом, который может подписываться на события и получать их от имени "реальных" потребителей. Публикации для всех уведомлений доставляются в прокси, который, в данном случае, отвечает за их доставку соответствующим потребителям (обычно после преобразования их в формат и/или транспортный протокол, требуемый для конкретного потребителя).


Рисунок 8. Основанная на прокси публикация/подписка
Рисунок 8. Основанная на прокси публикация/подписка

Application Server 6.1 предоставляет элегантное решение для реализации такого шаблона проектирования, позволяя связывать специальные параметры со ссылкой на потребителя. Фрагмент кода для создания специального параметра и назначения его ссылке на потребителя показан в листинге 14.


Листинг 14. Назначение пользовательских параметров ссылке на оконечную точку
                
EndpointReference consumerEPR = null;
QName pname = new QName("http://www.cna.com/data/name","name", "name");
try{
    URI epURI = new URI(ep);
    consumerEPR = EndpointReferenceManager.createEndpointReference(epURI);
			
    // Добавить специальный параметр к ссылке на потребителя 
    SOAPFactory soapFactory = SOAPFactory.newInstance();
    SOAPElement param = soapFactory.createElement("param", "param",
	 "http://www.cna.com/data/param");
    param.addTextNode("Some parameter data");
    consumerEPR.setReferenceParameter(pname,param);
}
catch(Exception e){
    System.out.println("Error creating notification URI");
    e.printStackTrace();
}
	  

Помещение кода, приведенного в листинге 14, в реализацию подписки (листинг 3) добавляет дополнительный элемент к consumerReference, что показано в листинге 15.


Листинг 15. Специальный элемент в запросе ссылки на потребителя
                
<ConsumerReference xmlns="http://docs.oasis-open.org/wsn/b-2">
    <wsa:Address xmlns:wsa="http://www.w3.org/2005/08/addressing">
        http://localhost:9080/notificationConsumer/services/NotificationConsumerPort
    </wsa:Address>
    <wsa:ReferenceParameters xmlns:wsa="http://www.w3.org/2005/08/addressing">
        <name:name xmlns:name="http://www.cna.com/data/name">
            <param:param xmlns:param="http://www.cna.com/data/param">
                Some parameter data
            </param:param>
        </name:name>
    </wsa:ReferenceParameters>
</ConsumerReference>
	  

Это специальное свойство затем распространяется с каждым уведомлением (для данной подписки) в форме SOAP-заголовка, как показано в листинге 16.


Листинг 16. Специальное свойство как SOAP-заголовок в уведомлении
                
<soapenv:Header>
	<name:name wsa:IsReferenceParameter="true"
			xmlns:name="http://www.cna.com/data/name"
			xmlns:wsa="http://www.w3.org/2005/08/addressing">
		<param:param xmlns:param="http://www.cna.com/data/param">
			Some parameter data
		</param:param>
	</name:name>
</soapenv:Header>
	  

Для доступа к этому заголовку реализация потребителя уведомлений должна реализовать методы интерфейса javax.xml.rpc.server.ServiceLifecycle, определенные в JAX-RPC, как показано в листинге 17.


Листинг 17. Изменения в потребителе уведомлений для доступа к специальным свойствам
                
public class NotificationConsumerBindingImpl 
                           implements NotificationConsumer, ServiceLifecycle{

    protected MessageContext context;

    public void init(Object ctx){

        System.out.println("Inside Service init method");
        // Инициализация контекста сообщения для использования сервиса
        ServletEndpointContext sc = (ServletEndpointContext)ctx;
        if(ctx != null)
            context = sc.getMessageContext();
        else
            context = null;
    }

    public void destroy(){

        System.out.println("Inside Service destroy method");
    }
	
    public void notify(NotificationMessage[] notificationMessage, SOAPElement[] any) 
	                                                throws java.rmi.RemoteException {

        System.out.println("Recieving Notification ...");
		
        try{
            SOAPMessageContext smc = (SOAPMessageContext) context;
            SOAPMessage sMessage = smc.getMessage();
            SOAPHeader header = sMessage.getSOAPHeader();
            if(header != null){
                Iterator elements = header.getChildElements();
                while(elements.hasNext()){
                    SOAPElement hElement = (SOAPElement)elements.next();
                    System.out.println("Additional SOAP header:");
                    OutputFormat ouf = new OutputFormat();
                    XMLSerializer serializer = new XMLSerializer(System.out,ouf);
                    serializer.serialize(hElement);
                    System.out.println(" ");
                }
            }
        }
        catch(Exception e){
            System.out.println("Error getting additional SOAP headers");
            e.printStackTrace();
        }
    .................................
    }
}
	  

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

Резюме: Полезные компромиссы ведут к повышенной гибкости

Использование брокера WS-Notification в WebSphere Application Server 6.1 позволяет реализовать очень гибкую, расширяемую, основанную на стандартах систему обмена сообщениями типа публикация/подписка в SOA. Она легко настраивается, используется и программируется.

Это также требует пересмотра некоторых существующих практических SOA-решений. Несмотря на многочисленные публикации сторонников противоположного взгляда, многие текущие реализации SOA используют строго типизированный обмен данными вместо семантического обмена сообщениями. Общий подход (не обязательно правильный), использующийся при проектировании интерфейса сервисов, состоит в определении его в понятиях Java-классов и последующем генерировании WSDL-файла на их основе. Такой подход минимизирует необходимость работы с XML и WSDL, а также перекладывает заботу о маршаллизации/демаршаллизации XML на исполняющую систему Web-сервисов, что облегчает жизнь разработчикам. Однако это обычно затрудняет способность к взаимодействию реализаций сервисов.

Прямое отображение локальной прикладной модели обычно приводит к большому количеству преобразований во всей системе. Использование спецификации WS-Notification, определяющей бизнес-сообщения как SOAPElement (XML-дерево), требует другого подхода к определению интерфейсов. Аналогично передовому опыту интеграции корпоративных приложений (enterprise application integration - EAI), этот тип архитектуры требует определений канонических сообщений, которые могут совместно использоваться всеми потребителями и провайдерами. Такие канонические сообщения обычно определяют корпоративную семантику (общий набор определений бизнес-объектов), которая позволяет взаимодействовать различным потребителям и провайдерам.

Кроме того, такой подход уменьшает роль исполняющей системы Web-сервисов для XML-обработки. Ответственность за программное преобразование SOAPElement в используемые прикладные объекты и наоборот ложится на разработчиков. Хотя с первого взгляда это выглядит затрудняющим реализацию сервисов, но, в конце концов, приводит к значительно более гибким системам. Такой подход устраняет использование XML (как очень затратную поддержку маршаллизации объектов) и освобождает мощные возможности семантической системы обмена XML-сообщениями. Участники системы обмена могут обрабатывать сообщения так, как им нужно (они могут выбрать только те элементы, которые им нужны). Более того, любые расширения системы обмена сообщениями, реализованные издателями, не оказывают влияния на существующих потребителей.




В начало


Загрузка

ОписаниеИмяРазмерМетод загрузки
J2EE-кодar-wasnotcode.zip750KBHTTP
Информация о методах загрузки


Ресурсы