Новые источники данных для Teiid, часть 1: используем DDL

в 12:37, , рубрики: java, jboss, teiid, метки: , ,

Те, кто сталкивались с необходимостью объединения нескольких источников данных, наверное, уже знают о JBoss Teiid, вводная статья о нём есть даже на хабре. Коротко говоря, эта система предназначена для для представления нескольких физических источников данных (например, СУБД) в виде одной виртуальной базы данных (virtual database, VDB) с доступом по SQL.

Штатно Teiid поддерживает многие источники данных, например, Oracle, DB2, M$ SQL Server, MySQL, PostgreSQL, SalesForce, но, вместе с тем, предоставляет также и удобные инструменты для работы с web-сервисами, XML, JSON. На базе этого инструментария можно легко построить доступ к несложному источнику данных (например, делать запросы в twitter, в поставке Teiid есть готовый пример), и обойтись при этом только DDL-описанием. Но для чего-либо посложнее уже требуется писать код.

В этой части мы рассмотрим способ описания с помощью DDL, в следующей будем писать транслятор.

В общем случае для организации доступа к источнику требуются две составных части: коннектор (connector) и транслятор (translator). Коннектор отвечает за непосредственно соединение, тогда как транслятор агрегирует исходные данные (запрос), отправляет их через соединение, предоставленное коннектором, получает ответ и преобразует его в понятную для Teeid форму.

Традиционно для иллюстрации описываемого в постах на хабре авторы используют что-нибудь, имеющее отношение к хабру. Почему бы и нет? В качестве источника данных мы возьмём хабр-апи: http://habrahabr.ru/api/profile/%name%.

Среди прочих коннекторов, поставляемых с Teiid, есть универсальный WS connector, подходящий для любого сервиса, предоставляющего доступ по http/https. Им мы и воспользуемся для доступа к web-сервису хабра.

Базовые настройки

Для начала необходимо прописать источник данных. Для JBoss 7 в файл %JBOSS_HOME%/standalone/configuration/standalone.xml (или ...domain...) нужно добавить такое:

<subsystem xmlns="urn:jboss:domain:resource-adapters:1.0">
[...]
    <resource-adapters>
        <resource-adapter>
            <archive>teiid-connector-ws.rar</archive>
            <transaction-support>NoTransaction</transaction-support>
            <connection-definitions>
                <connection-definition class-name="org.teiid.resource.adapter.ws.WSManagedConnectionFactory" jndi-name="java:/habrDS" enabled="true" use-java-context="true" pool-name="habr-ds"><!-- [1] -->
                      <config-property name="EndPoint">http://habrahabr.ru/api/profile/</config-property><!-- [2] -->
                </connection-definition>
            </connection-definitions>
        </resource-adapter>
    </resource-adapters>
[...]

Тут есть 2 важных момента: в строке с отметкой [1] мы задаем JNDI-имя нашего data source (это имя мы будем использовать в дальнейшем), а в строке с отметкой [2] — end point — URL нашего сервиса.

Пишем DDL

Я уже упоминал выше о примере реализации запросов в twitter, который поставляется с Teiid. Для того, чтобы получить доступ к хабр-апи таким же способом, достаточно написать правильный DDL в VDB-файле.

Итак, habr-vdb.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vdb name="habr" version="1">
    <model name="habr">
        <source name="habr" translator-name="rest" connection-jndi-name="java:/habrDS"/>
    </model>
    <model name="habrview" type="VIRTUAL">
         <metadata type="DDL"><![CDATA[
             CREATE VIRTUAL PROCEDURE getHabr(name varchar) RETURNS (login varchar(128), karma float, rating float, 
                 ratingposition long) AS 
                select ha.* from 
                    (call habr.invokeHTTP(action => 'GET', endpoint =>querystring(name))) w, 
                    XMLTABLE('habrauser' passing XMLPARSE(document w.result) columns 
                    login varchar(128) PATH 'login',
                    karma float PATH 'karma',
                    rating float PATH 'rating',
                    ratingposition long PATH 'ratingPosition') ha;
                CREATE VIEW Habr AS select * FROM habrview.getHabr;
        ]]> </metadata>
    </model>
    <translator name="rest" type="ws">
        <property name="DefaultBinding" value="HTTP"/>
        <property name="DefaultServiceMode" value="MESSAGE"/>
    </translator>
</vdb>

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

Разбираем код подробнее

<translator name="rest" type="ws"/> — способ описание нового транслятора через наследование (type — имя родительского транслятора, name — имя создаваемого транслятора). Предназначен этот способ для того, чтобы можно было задавать значение свойств (properties), отличных от значений по умолчанию. Т.к. значение по умолчанию свойства DefaultBinding = SOAP12, то мы не можем напрямую использовать транслятор ws.

Штатный WS connector реализует процедуру invokeHTTP(), через которую и работает весь его функционал. <model name="habr"/> служит для подключения нашего источника данных habrDS (мы описывали его выше, в standalone.xml) и вновь созданного транслятора 'rest'. Таким образом, при вызове habr.invokeHTTP() мы вызываем транслятор с конкретными параметрами и заданным URL web-сервиса.

Для непосредственной обработки данных мы должны создать дополнительную модель — <model name="habrview" type="VIRTUAL"/>: дело в том, что описывать сущности БД через DDL мы можем только в моделях, имеющих type="VIRTUAL", с другой стороны, указывать источники данных и трансляторы мы можем только в не-виртуальных моделях, а нам необходимо и то, и другое.

Здесь всё просто: создаём виртуальную процедуру getHabr, для которой описываем входной параметр и результаты, и которая реализуется через SELECT-запрос, который, в свою очередь, вызывает habr.invokeHTTP() для выполнения GET-запроса к web-сервису (с назначением результату алиаса w). Здесь параметр name передаётся из виртуальной процедуры в реальную. habr.invokeHTTP() возвращает полученные данные в параметре result, который далее передаётся во встроенную функцию XMLPARSE(document w.result), где document означает, что это well-formed XML, а не фрагмент. Эта функция парсит полученные данные и уже в виде XML-дерева передаёт дальше, в функцию XMLTABLE, для которой мы так же, как и для виртуальной процедуры, задаём список колонок с указанием типов, а кроме типов указываем ещё и пути, по которым значения будут доставаться из XML-документа. 'habrauser' означает базовый путь.

И последний шаг: создаём view, которое реализуется, как вызов виртуальной процедуры и, соответственно, заимствует список её выходных параметров в качестве своей структуры.

Всё. Осталость только сделать запрос:

select * from habrview.habr where name='elfuegobiz'

Автор: elfuegobiz

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js