вторник, 4 декабря 2012 г.

Пример использования Spring Framework совместно с SCA-контейнером Oracle WebLogic

В одной из предыдущих заметок я привел теоретическое описание компонентной архитектуры сервисов (SCA). Однако любая теория требует практического подкрепления, поэтому в данной заметке мы рассмотрим использование SCA-контейнера сервера приложений Oracle WebLogic и Spring Framework для реализации некоторых из довольно часто используемых паттернов интеграции корпоративных приложений (EIP) - Обогатитель данных (Data Enricher) и основанный на нем паттерн Квитанция (Claim Check).

Паттерн Обогатитель данных



Если есть две интегрируемые системы, одна из которых возвращает меньше данных, нежели нужно другой, то такие данные требуется обогащать - добавлять недостающие, получая их из некоторого хранилища. Например, по почтовому индексу, передаваемому системой-источником, можно получить во внешнем хранилище, например в БД КЛАДР, населенный пункт абонента и передать его в систему-приемник. В данном случае Обогатитель данных выступает специальным трансформатором преобразующим сообщение с неполным набором данных в итоговое сообщение. Важным компонентом данного паттерна является внешнее хранилище данных, которое может представлять собой СУБД, некоторое предустановленное корпоративное приложение или публичную службу.

Паттерн Квитанция




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


Обзор реализации паттерна Квитанция с помощью компонентной архитектуры сервисов


Компонентная архитектура сервисов позволяет сделать следующее:

  • Инкапсулировать бизнес-логику в некотором ограниченном участке кода - в компоненте.

  • Сделать данный компонент доступным другим компонентам, выставив его в качестве сервиса (в терминологии SCA - service).

  • Использовать в компоненте логику, реализованную в других компонентах, подключаясь к их сервисам с помощью механизма ссылок (в терминологии SCA - reference).


Для реализации паттерна Квитанция разработаем систему, состоящую из следующих частей:

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

  • MessageProcessor - SCA-сборка, реализующая паттерн Квитанция. Данная сборка содержит компонент MessageProcessorService, выставляемый в качестве EJB. Именно через данный сервис осуществляется использование SCA-сборки в CanonicalQueueClient. Обогащение сообщения осуществляется в MessageEnricher, подключение к которому производится с помощью ссылки, использующей EJB-связывание. После обогащения сообщение отправляется в систему-приемник с помощью DestinationEndpoint, подключение к которому производится так же с помощью ссылки, использующей EJB-связывание.

  • MessageEnricher представляет собой сессионный компонент без сохранения состояния (EJB Stateless Session Bean), задачей которого является возвратить содержимое сообщения по его идентификатору. Для связи с хранилищем сообщений используется JPA.

  • DestinationEndpoint представляет собой сессионный компонент без сохранения состояния, осуществляющий доставку сообщения в систему-приемник. Связь с системой-приемником осуществляется через буферную таблицу. Для записи сообщений в данную таблицу используется JPA.


Схематично взаимосвязи между участниками процесса можно отобразить на схеме SCA-сборки MessageProcessor.


Компонент MessageProcessor


Сердцем описанной системы является компонент MessageProcessor, представляющий собой SCA-сборку, основанную на Spring Framework и работающую под управлением SCA-контейнера сервера приложений Oracle WebLogic. Данная сборка с точки зрения сервера приложений представляет собой веб-приложение, разворачиваемое на сервере в виде архива веб-приложения, т.е. WAR-файла. Отличие от стандартного приложения заключается в том, что в каталоге WEB-INF/classes/META-INF архива находится подкаталог jsca, содержащий файл spring-context.xml. В данный файл помещается контекст приложения, основанного на Spring Framework, а так же определения сервисов и ссылок в терминах SCA.

В интегрированной среде разработки Eclipse SDK структура данного приложения выглядит следующим образом:


Набор "граней" (facets) данного проекта Eclipse SDK обязательно включает в себя Dynamic Web Module, Spring и самое главное - Oracle WebLogic SCA.


Рассмотрим файл spring-context.xml подробнее:

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

<beans xmlns="http://www.springframework.org/schema/beans"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"

      xmlns:wlsb="http://xmlns.oracle.com/weblogic/weblogic-sca-binding"

      xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"

      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

           http://xmlns.oracle.com/weblogic/weblogic-sca http://xmlns.oracle.com/weblogic/weblogic-sca/1.0/weblogic-sca.xsd

           http://xmlns.oracle.com/weblogic/weblogic-sca-binding http://xmlns.oracle.com/weblogic/weblogic-sca-binding/1.0/weblogic-sca-binding.xsd

      ">


    <sca:service name="MessageProcessorService" type="name.samolisov.message.processor.MessageProcessorService"

                 target="MessageProcessor">

        <wlsb:binding.ejb name="MessageProcessor" remote="true" uri="messageprocessor"/>

        <wlsb:binding.ws port="MessageProcessor" uri="/adapter" soapVersion="1.1" name="MessageProcessorService"/>     

    </sca:service>

   

    <sca:reference name="MessageEnricher" type="name.samolisov.message.enricher.MessageEnricherService">

        <wlsb:binding.ejb name="MessageEnricher" remote="true" uri="messagenricher#name.samolisov.message.enricher.MessageEnricherServiceRemote"/>

    </sca:reference>

   

    <sca:reference name="AdapterEndpoint" type="name.samolisov.adapter.endpoint.AdapterEndpoint">

        <wlsb:binding.ejb name="AdapterEndpoint" remote="true" uri="destination#name.samolisov.adapter.endpoint.AdapterEndpoint"/>

    </sca:reference>

   

    <bean id="MessageProcessor" class="name.samolisov.message.processor.MessageProcessorServiceImpl">

        <property name="src" value="SAP"/>

        <property name="dst" value="OEBS"/>

        <property name="endpoint" ref="AdapterEndpoint"/>

        <property name="enricher" ref="MessageEnricher"/>

    </bean>

</beans>

 

Прежде всего следует отметить, что в данном файле определяются пространства имен, специфичные для SCA-контейнера сервера приложений Oracle WebLogic:

  • http://xmlns.oracle.com/weblogic/weblogic-sca - в данном пространстве имен определены все специфичные для SCA элементы, прежде всего service и reference.

  • http://xmlns.oracle.com/weblogic/weblogic-sca-binding - в данном пространстве имен определяются параметры связывания.


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

  • name - наименование сервиса;

  • type - полное наименование Java-типа интерфейса сервиса;

  • target - ссылка на компонент, создаваемый с помощью Spring Framework и реализующий бизнес-логику сервиса.


    <sca:service name="MessageProcessorService" type="name.samolisov.message.processor.MessageProcessorService"

                 target="MessageProcessor">

        <wlsb:binding.ejb name="MessageProcessor" remote="true" uri="messageprocessor"/>

        <wlsb:binding.ws port="MessageProcessor" uri="/adapter" soapVersion="1.1" name="MessageProcessorService"/>     

    </sca:service>



 

Ссылка на внешний компонент объявляется с помощью элемента sca:reference. Данный элемент содержит следующие атрибуты:

  • name - наименование ссылки;

  • type - полное наименование Java-типа интерфейса ссылки;

  • default - идентификатор или наименование компонента, создаваемого с помощью Spring Framework'а, который будет использоваться в случае неудачи связывания ссылки со внешним компонентом. Данный атрибут является необязательным.


    <sca:reference name="MessageEnricher" type="name.samolisov.message.enricher.MessageEnricherService">

        <wlsb:binding.ejb name="MessageEnricher" remote="true" uri="messagenricher#name.samolisov.message.enricher.MessageEnricherServiceRemote"/>

    </sca:reference>

 

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

    <bean id="MessageProcessor" class="name.samolisov.message.processor.MessageProcessorServiceImpl">

        <property name="src" value="SAP"/>

        <property name="dst" value="OEBS"/>

        <property name="endpoint" ref="AdapterEndpoint"/>

        <property name="enricher" ref="MessageEnricher"/>

    </bean>

В данном определении AdapterEndpoint и MessageEnricher являются ссылками на внешние компоненты.

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

package name.samolisov.message.processor;



import name.samolisov.adapter.endpoint.AdapterEndpoint;

import name.samolisov.message.enricher.Check;

import name.samolisov.message.enricher.Message;

import name.samolisov.message.enricher.MessageEnrichException;

import name.samolisov.message.enricher.MessageEnricherService;



public class MessageProcessorServiceImpl implements MessageProcessorService {

   

    private String src;

   

    private String dst;

   

    private AdapterEndpoint endpoint;

   

    private MessageEnricherService enricher;

   

    @Override

    public void process(Long id) throws MessageProcessingException {       

        try {

            Message message = enricher.enrich(new Check(id, src, dst));

            if (message != null)

                endpoint.save(id, message.getBody());

            else

                throw new MessageProcessingException("Could not process message with id " + id);

        } catch (MessageEnrichException e) {

            throw new MessageProcessingException("Could not process message with id " + id, e);

        }                  

    }



    public AdapterEndpoint getEndpoint() {

        return endpoint;

    }



    public void setEndpoint(AdapterEndpoint endpoint) {

        this.endpoint = endpoint;

    }



    public MessageEnricherService getEnricher() {

        return enricher;

    }



    public void setEnricher(MessageEnricherService enricher) {

        this.enricher = enricher;

    }



    public String getSrc() {

        return src;

    }



    public void setSrc(String src) {

        this.src = src;

    }



    public String getDst() {

        return dst;

    }



    public void setDst(String dst) {

        this.dst = dst;

    }  

}

 

Связывание компонента с внешним миром


Осталось рассмотреть связывание сервисов и ссылок с соответствующими внешними объектами. SCA-контейнер сервера приложений Oracle WebLogic поддерживает два типа связывания: с веб-сервисами и с сессионными компонентами без сохранения состояния. Задаются данные типы связывания с помощью элементов wlsb:binding.ws и wlsb:binding.ejb, помещенных внутрь элементов sca:service и sca:reference.

Элемент wlsb:binding.ws отвечает за связывание посредством веб-сервисов и содержит следующие атрибуты:

  • port - наименование порта в генерируемом WSDL-файле;

  • uri - относительный путь к оконечной точке сервиса;

  • soapVersion - версия SOAP-протокола, поддерживаемая сервисом;

  • name - наименование сервиса в генерируемом WSDL-файле. Если данный атрибут не указать, то значение атрибута name в заголовке WSDL-файла будет выставлено в DummyWsdlFileName, а значение атрибута name элемента wsdl:service будет равно идентификатору сервиса для которого определяется связывание.


Имя порта (атрибут name элемента wsdl:portType) будет совпадать с наименованием класса, заданного в качестве значения атрибута type определения сервиса или ссылки.

По-умолчанию наименования параметров операций генерируются вида arg0, arg1 и т.д. Переопределить наименования операций, параметров и возвращаемых значений можно с помощью аннотаций @WebMethod, @WebResult и @WebParam, используя их при описании интерфейса сервиса.

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

  • name - наименование связывания.

  • remote - данный атрибут не описан в документации, судя по названию он должен отвечать за то, используется ли связывание с локальным или удаленным компонентом. В данное время поддерживается связывание только с удаленными компонентами.

  • uri - JNDI-наименование компонента. В случае связывания с EJB необходимо указывать полное наименование, совпадающее с отображаемым в JNDI-дереве, например messagenricher#name.samolisov.message.enricher.MessageEnricherServiceRemote.

  • dispatchPolicy - данный атрибут указывается только для связывания, используемого при определении сервиса. Позволяет задать WorkManager, потоки для которого будут использоваться при обработке запросов к данному сервису.


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

Разворачивание SCA-сборки


Для работы SCA-контейнера необходимо установить на сервер приложений разделяемую библиотеку weblogic-sca, дистрибутив которой расположен в каталоге %MIDDLEWARE_HOME%\wlserver_10.3\common\deployable-libraries.


Разворачивание осуществляется аналогично разворачиванию любого другого веб-приложения. Если приложение взаимодействует с разработанными специально для него EJB-компонентами, то разумно поместить его и данные компоненты в один архив корпоративного приложения - EAR-файл.


Для упрощения мониторинга компонентов, в состав консоли управления WebLogic входит расширение weblogic-sca-console, включить которое можно на вкладке Extensions страницы Preferences.


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

Теперь на странице корпоративного приложения (это важно, не SCA-сборки, а корпоративного приложения, в которое она входит, EAR-файла) появится вкладка WebLogic SCA, на которой будет отображен список файлов с определениями компонентов, а также сервисы и ссылки, определенные в каждом файле.


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


Страница для ссылки попроще: отображается только наименование данной ссылки и список методов интерфейса.


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

<message xmlns="urn:check">

  <id>1</id>

</message>

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


Система так же работает и по веб-сервису:


В состав Oracle Fusion Middleware помимо SCA-контейнера сервера приложений WebLogic входит и SCA-контейнер Oracle SOA Suite, обладающий гораздо большими возможностями, но также содержащий средства интеграции со Spring Framework'ом. Подробнее об использовании данного довольно мощного продукта поговорим как-нибудь в следующий раз. Оставайтесь на связи!

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

2 комментария:

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

Интересная статья, спасибо.
А можно выложить куда-нибудь исходники примера?

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

Выложил на GitHub.

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

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