Веб-сервисы в Oracle



    Веб-сервисы широко применяются для интеграции между компонентами одной системы или между различными системами. Популярность веб-сервисов как способа интеграции обусловлена их универсальностью, а также простотой реализации и отладки. Универсальность связана с передачей данных с помощью интернета и протокола HTTP. Веб-сервисы дают возможность относительно легко построить интеграции между компонентами, написанными на разных языках, запускаемыми на разных операционных системах и платформах. Простота реализации веб-сервисов достигается за счет встроенных во многие IDE средств и компонентов, позволяющих быстро разработать как сам веб-сервис (provider side), так и необходимый код для вызова сервиса на стороне клиента (client side). Отладку сервисов упрощает использование понятных человеку форматов обмена данными — XML и JSON. Кроме того, существует множество утилит для отладки и тестирования сервисов, в том числе нагрузочного.

    В этой статье рассмотрим несколько способов создания веб-сервисов непосредственно из СУБД Oracle, то есть без использования сторонних средств.

    Содержание
    Предыстория
    Native Oracle XML DB Web Services
        Возможности
        Настройка
            Настройка Oracle XML DB HTTP server
            Настройка доступа через HTTP
            Создание сервлета для веб-сервиса
            Настройка Access List (ACL)
        Пример веб-сервиса с использованием пакетной процедуры
        Выводы
    Oracle REST Data Service
        Возможности
        Настройка
            Настройка параметров
            Установка
            Запуск
            Настройка доступа
        Пример 1: Обработчик для POST-запроса
            Создание тестового пользователя для WS
            Настройка схемы БД
            Создание модуля и шаблона
            Создание обработчика HTTP-запроса
            Пример вызова
            Параметры из запроса
            CGI-переменные
        Пример 2: Доступ к таблице через ORDS
            Создание тестовой таблицы
            Открытие доступа к таблице через ORDS
            Создание или изменение записи
            Получение записей из таблицы
            Удаление записи
        Выводы
    Альтернативные подходы
        Java Servlet
        Database Access Descriptor (PL/SQL Servlet)
    Заключение

    Предыстория


    Дано: информационная бизнес-система крупной торговой сети (порядка тысячи розничных магазинов), состоящая из множества компонентов и подсистем. Внутри каждого магазина находится главный сервер — БД Oracle с основной бизнес-логикой. Кассовые узлы управляются отдельным ПО со своей локальной БД. Это ПО периодически забирает данные с главного сервера (через WS SOAP) и отдает обратно результаты продажи (файловый обмен).

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

    В итоге было рассмотрено несколько вариантов реализации нужного веб-сервиса, вплоть до написания своего отдельного компонента, который открыл бы для БД Oracle окно в мир HTTP: с одной стороны предоставлял бы веб-сервис, с другой — взаимодействовал бы с БД через JDBC. Сложность в том, что новый компонент, во-первых, нужно было бы установить на тысячу магазинов, и, во-вторых, появилось бы еще одно звено, которое нужно было бы сопровождать. Поэтому приоритетным все же был вариант реализации веб-сервиса встроенными средствами Oracle.

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

    1. Native Oracle XML DB Web Services
    2. Oracle REST Data Service
    3. Java Servlet
    4. Database Access Descriptor (PL/SQL servlet)

    Первые два варианта рассмотрим более детально. Native Oracle XML DB Web Services использовался в нашей системе изначально, то есть достался, так сказать, по наследству. ORDS стал заменой этой устаревшей технологии (несмотря на то, что пришлось все же потрудиться и установить ORDS на тысячу магазинов).

    Два других способа — Java Servlet и PL/SQL Servlet — мы рассматривали наряду с ORDS при поиске альтернативы Native Oracle WS, но в проекте применять не стали. Поэтому эти подходы детально рассматривать не будем и ограничимся короткой справкой.

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

    Native Oracle XML DB Web Services


    Возможности


    Позволяет организовать доступ к БД Oracle через HTTP, используя WS SOAP (версия 1.1):

    • Выполнение SQL- или XQuery-запросов через SOAP.
    • Вызов хранимых процедур или функций (отдельных или из пакета).

    Плюсы

    • Для поднятия веб-сервиса не требуется установка дополнительного ПО: все необходимое уже имеется в СУБД, включая встроенный HTTP-сервер.
    • Для веб-сервиса автоматически формируется описание (WSDL), которое может быть использовано потребителем сервиса для автоматической генерации кода, вызывающего веб-сервис.
    • Позволяет быстро реализовать или доработать веб-сервис. По сути, создание нового сервиса — это реализация нового пакета, а создание нового метода сервиса — это написание новой процедуры или функции в пакете.
    • Автоматический парсинг входящих XML-запросов. С одной стороны, это плюс, потому что не нужно думать о структуре XML, реализовывать свои парсеры и следить за их актуальностью при изменении спецификации веб-сервиса — все это Oracle делает автоматически. С другой стороны, к этой внутренней кухне преобразования WS-запроса в вызов процедуры Oracle нет никакого доступа —никак нельзя переопределить эти алгоритмы, если вдруг понадобится что-то оптимизировать.

    Минусы

    • Автоматически генерируемый WSDL имеет ряд ограничений, и нет возможности править его вручную, например указать необязательность каких-то атрибутов. Для решения этой проблемы есть обходной путь — вместо автоматически генерируемого использовать сформированный вручную WSDL, но тогда придется написать текст WSDL самостоятельно, придерживаясь правил именования атрибутов.
    • Может вести себя непредсказуемо на больших запросах (порядка 50 Мб). В лучшем случае падает с ошибкой нехватки памяти на сервере, в худшем — отдает ответ, в котором потеряна часть сообщения. Вообще, большие запросы при использовании веб-сервисов — это скорее исключение, чем правило, но лучше подстраховаться.

    Стоит отметить, что для всех запросов к веб-сервису обязательна аутентификация (из документации: Basic Authentication: Oracle XML DB supports Basic Authentication, where a client sends the user name and password in clear text in the Authorization header). В Oracle можно настроить анонимный доступ к ресурсам сервера через HTTP — с помощью настройки Oracle XML DB Protocol Server, но по факту это работает только для GET-запросов, а для POST-запросов аутентификация обязательна. Поскольку Native Oracle WS работает только через POST-запросы, возможности настройки анонимного доступа для этой технологии нет.

    Настройка


    Для работы Native Oracle WS понадобится:

    1. Настроить встроенный в Oracle HTTP-сервер.
    2. Настроить доступ внутри БД Oracle (открыть HTTP-порт).
    3. Создать сервлет.
    4. Настроить ACL (Access List).

    Настройка Oracle XML DB HTTP server


    HTTP-сервер уже должен работать по умолчанию, но в некоторых случаях может потребоваться дополнительная конфигурация listener — добавление в DESCRIPTION_LIST следующего блока:

    (DESCRIPTION=
      (ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=8080))(Presentation=HTTP)(Session=RAW)
    )

    После этого нужно перезапустить listener.

    Настройка доступа через HTTP


    1. Проверка текущего порта для HTTP.

      SELECT dbms_xdb.gethttpport() AS http_port FROM dual;

      Значение «0» означает, что доступ через HTTP отключен.
    2. Установка порта.

      BEGIN
          -- Установка порта для web-services
          dbms_xdb.setHttpPort(8080);
          COMMIT;
      END;
      /

    Создание сервлета для веб-сервиса


    Для работы веб-сервиса требуется регистрация сервлета в конфигурации БД.

    Скрипт для создания сервлета
    -- Выполнять под SYS
    DECLARE
        l_servlet_name      VARCHAR2(32)  := 'orawsv';
    BEGIN
        -- Регистрация сервлета orawsv для активации Native Web Services
        DBMS_XDB.deleteServletMapping(l_servlet_name);
        DBMS_XDB.deleteServlet(l_servlet_name);
     
        DBMS_XDB.addServlet( NAME     => l_servlet_name
                           , LANGUAGE => 'C'
                           , DISPNAME => 'Oracle Query Web Service'
                           , DESCRIPT => 'Servlet for issuing queries as a Web Service'
                           , SCHEMA   => 'XDB');
     
        DBMS_XDB.addServletSecRole( SERVNAME => l_servlet_name
                                  , ROLENAME => 'XDB_WEBSERVICES'
                                  , ROLELINK => 'XDB_WEBSERVICES');
     
        DBMS_XDB.addServletMapping( PATTERN => '/orawsv/*'
                                  , NAME    => l_servlet_name);
        COMMIT;
    END;
    /

    Настройка Access List


    Для доступа к Oracle через HTTP нужно добавить правила в конфигурации СУБД. Делается это с помощью встроенных утилит СУБД.

    Для настройки ACL понадобятся:

    • cкрипт, редактирующий конфигурацию БД (ниже);
    • cхема БД, для которой делается настройка.

    То есть схема БД, для которой делается настройка ACL, должна быть уже создана. В примерах ниже будут отсылки к этому разделу, в тех местах, где необходимо создать новые схемы БД — для них нужно будет выполнять настройку ACL.

    В схеме SYS выполняем скрипт настройки ACL:
    -- Выполнять под SYS
    DECLARE
        l_ws_user       VARCHAR2(32)  := 'WS_SOAP_TEST'; -- Указать имя схемы БД, где предполагается развернуть объекты для обработки запросов WS
        l_acl           VARCHAR2(250) := 'acl_allow_all.xml';
        l_tmp           VARCHAR2(250);
    BEGIN
        EXECUTE IMMEDIATE 'GRANT XDB_WEBSERVICES TO "'||l_ws_user||'"';
        EXECUTE IMMEDIATE 'GRANT XDB_WEBSERVICES_OVER_HTTP TO "'||l_ws_user||'"';
        EXECUTE IMMEDIATE 'GRANT XDB_WEBSERVICES_WITH_PUBLIC TO "'||l_ws_user||'"';
     
        -- Создание списка разрешений сетевого доступа из PL/SQL пакетов
        BEGIN
            dbms_network_acl_admin.drop_acl(acl => '/sys/acls/'||l_acl);
        EXCEPTION
            WHEN dbms_network_acl_admin.acl_not_found THEN
                NULL;
        END;
     
        -- Создание ACL
        dbms_network_acl_admin.create_acl( acl         => l_acl
                                         , description => 'Allow all connections'
                                         , is_grant    => TRUE
                                         , start_date  => SYSTIMESTAMP
                                         , end_date    => NULL
                                         , principal   => 'SYS'
                                         , privilege   => 'connect');
     
        dbms_network_acl_admin.assign_acl( acl        => l_acl
                                         , host       => '*'
                                         , lower_port => NULL
                                         , upper_port => NULL);
     
        -- Права на разрешение (resolve)сетевого имени
        dbms_network_acl_admin.add_privilege( acl        => l_acl
                                            , principal  => l_ws_user
                                            , is_grant   => TRUE
                                            , privilege  => 'resolve'
                                            , POSITION   => NULL
                                            , start_date => SYSTIMESTAMP
                                            , end_date   => NULL);
     
        COMMIT;
    END;
    /

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

    Пример веб-сервиса с использованием пакетной процедуры


    Нам понадобятся:

    • Схема БД, в которой будут находиться объекты для обработки запросов веб-сервиса.
    • Таблица для логгирования.
    • Пакет с процедурой/функцией.
    • Любое средство, позволяющее отправлять веб-запросы. В данном случае использовалось приложение SOAP UI, но можно выбрать любое другое средство, вплоть до командной строки.

    Особенности:

    • Параметры процедуры должны иметь либо простые, либо объектные типы. Иначе процедура не будет восприниматься как метод веб-сервиса и не будет включена в список методов сервиса.
    • Для реализации сложных структур входных или выходных данных нужно использовать объектные типы (пакетные типы использовать нельзя).

    Создаем необходимые объекты БД:

    1. Схема WS_TEST:

      CREATE USER WS_SOAP_TEST IDENTIFIED BY ws_soap_test QUOTA 200M ON USERS;
      GRANT CREATE SESSION, RESOURCE TO ws_soap_test;

      Сразу добавляем новую схему в ACL. Скрипт в предыдущем разделе.
    2. В новой схеме создаем таблицу для логгирования T_WS_REQ_LOG:

      CREATE TABLE T_WS_REQ_LOG
      (
        id_log      NUMBER GENERATED ALWAYS AS IDENTITY,
        message     VARCHAR2(2000),
        proc        VARCHAR2(128),
        dtm_request TIMESTAMP(6) DEFAULT SYSTIMESTAMP
      );
      COMMENT ON TABLE T_WS_REQ_LOG IS 'Лог HTTP-запросов';
      COMMENT ON COLUMN T_WS_REQ_LOG.id_log IS 'ПК';
      COMMENT ON COLUMN T_WS_REQ_LOG.message IS 'Сообщение';
      COMMENT ON COLUMN T_WS_REQ_LOG.proc IS 'Процедура';
      COMMENT ON COLUMN T_WS_REQ_LOG.dtm_request IS 'Дата/время запроса';
    3. Пакет с простой процедурой:

      
      CREATE OR REPLACE PACKAGE PK_NATIVE_WS_TEST IS
       
      PROCEDURE proc_simple
          ( a_id      INTEGER
          , a_data    VARCHAR2
          , o_result  OUT VARCHAR2
          );
       
      END PK_NATIVE_WS_TEST;
      /
      CREATE OR REPLACE PACKAGE BODY PK_NATIVE_WS_TEST IS
       
      PROCEDURE proc_simple
          ( a_id      INTEGER
          , a_data    VARCHAR2
          , o_result  OUT VARCHAR2
          )
      AS
      BEGIN
          INSERT INTO t_ws_req_log (message, proc)
          VALUES ('ID='||a_id||'; DATA='||a_data, 'proc_simple')
          RETURNING id_log INTO o_result;
      END proc_simple;
       
      END PK_NATIVE_WS_TEST;
      /

    Простейший веб-сервис создан и готов к работе.

    Для доступа к сервису нужно использовать URL следующего формата:

    http://[server]:[port]/[servlet_name]/[DB_SCHEMA]/[WS_OBJ]?wsdl

    Где:

    [server] — доменное имя или IP-адрес сервера БД Oracle
    [port] — порт для доступа через HTTP, указанный в разделе «Настройка доступа через HTTP»
    [servlet_name] — имя сервлета, указанное в разделе «Создание сервлета для веб-сервиса»
    [DB_SCHEMA] — имя схемы БД (в верхнем регистре)
    [WS_OBJ] — имя сервиса (в верхнем регистре), которое равно имени объекта БД, в нашем случае – имени пакета

    Обратите внимание, что в URL регистр важен!

    Пример ссылки:

    http://my.server:8080/orawsv/WS_SOAP_TEST/PK_NATIVE_WS_TEST?wsdl
    

    Если перейти по этой ссылке в браузере, получим автоматически созданный на основе нашего пакета WSDL:

    WSDL
    <definitions name="PK_NATIVE_WS_TEST"
        targetNamespace="http://xmlns.oracle.com/orawsv/WS_SOAP_TEST/PK_NATIVE_WS_TEST"
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        xmlns:tns="http://xmlns.oracle.com/orawsv/WS_SOAP_TEST/PK_NATIVE_WS_TEST"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
      <types>
        <xsd:schema targetNamespace="http://xmlns.oracle.com/orawsv/WS_SOAP_TEST/PK_NATIVE_WS_TEST"
         elementFormDefault="qualified">
          <xsd:element name="PROC_SIMPLEInput">
            <xsd:complexType>
                <xsd:sequence>
                  <xsd:element name="A_DATA-VARCHAR2-IN" type="xsd:string"/>
                  <xsd:element name="A_ID-NUMBER-IN" type="xsd:integer"/>
                </xsd:sequence>
              </xsd:complexType>
          </xsd:element>
     
          <xsd:element name="PROC_SIMPLEOutput">
            <xsd:complexType>
                <xsd:sequence>
                  <xsd:element name="O_RESULT" type="xsd:string"/>
                </xsd:sequence>
              </xsd:complexType>
          </xsd:element>
       </xsd:schema>
      </types>
     
      <message name="PROC_SIMPLEInputMessage">
        <part name="parameters" element="tns:PROC_SIMPLEInput"/>
      </message>
     
      <message name="PROC_SIMPLEOutputMessage">
        <part name="parameters" element="tns:PROC_SIMPLEOutput"/>
      </message>
     
      <portType name="PK_NATIVE_WS_TESTPortType">
      <operation name="PROC_SIMPLE">
          <input message="tns:PROC_SIMPLEInputMessage"/>
          <output message="tns:PROC_SIMPLEOutputMessage"/>
        </operation>
      </portType>
     
      <binding name="PK_NATIVE_WS_TESTBinding"
               type="tns:PK_NATIVE_WS_TESTPortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="PROC_SIMPLE">
          <soap:operation
     soapAction="PROC_SIMPLE"/>
          <input>
            <soap:body parts="parameters" use="literal"/>
          </input>
          <output>
            <soap:body parts="parameters" use="literal"/>
          </output>
        </operation>
      </binding>
     
      <service name="PK_NATIVE_WS_TESTService">
        <documentation>Oracle Web Service</documentation>
        <port name="PK_NATIVE_WS_TESTPort" binding="tns:PK_NATIVE_WS_TESTBinding">
           <soap:address 
                 location="http://******:8080/orawsv/WS_SOAP_TEST/PK_NATIVE_WS_TEST"/>
         </port>
      </service>
     
    </definitions>

    Для проверки сервиса создаем новый SOAP-проект в SOAP UI. В качестве URL указываем адрес из примера выше.

    При открытии Request 1 видим, что шаблон запроса уже сформирован автоматически по указанному WSDL.

    Сразу же добавляем Basic-авторизацию для запроса — логин и пароль такие же, как при подключении в БД.



    Пример запроса и ответа. Обратите внимание, что в автоматически сформированный запрос вручную добавлена строка <pk:O_RESULT-VARCHAR2-OUT/>. Дело в том, что в Oracle 12c есть баг: OUT-переменные не добавляются в WSDL для Native Oracle WS).



    Результат вызова видим в таблице лога.



    Выводы


    Технологию Native Oracle XML DB Web Services можно использовать как промышленное решение при условии, что на этот веб-сервис нет большой нагрузки (один запрос в несколько секунд). Этот вариант подойдет, когда у потребителей нет жестких требований к веб-сервису (его атрибутам, структурам данных, логике обработки) и нет строгого заранее определенного контракта. Если же нужно создать веб-сервис по заранее определенному WSDL (как в нашем случае с новым оборудованием), то технология Native Oracle WS окажется непригодна.

    Oracle REST Data Service


    Начиная с версии 11.1 в Oracle появилась полноценная поддержка RESTful в виде отдельного модуля, называемого Oracle REST Data Service (ORDS).

    ORDS — это Java-приложение, которое позволяет создавать RESTful API для базы данных Oracle, используя SQL и PL/SQL. Является альтернативой использованию Oracle HTTP Server и mod_plsql. По сути ORDS — это HTTP-интерфейс между внешним миром и БД Oracle. Этот интерфейс позволяет замкнуть входящие HTTP-запросы на какой-либо объект базы данных — таблицу или PL/SQL-процедуру.

    Все, что требуется сделать, — это установить, настроить и запустить ORDS для требуемой БД. Дальнейший процесс создания REST-сервисов сводится к написанию кода на PL/SQL (или даже кликанью мышкой в IDE, если код писать лень).

    Для установки ORDS не требуется никаких дополнительных лицензий.

    Требования:

    • Java JDK 8 или выше;
    • Oracle 11.1 или выше (также поддерживается Oracle 11 XE Release 2).

    Поддерживается несколько вариантов развертывания ORDS:

    • автономный (standalone) режим;
    • на сервере приложений (Oracle WebLogic Server, Apache Tomcat).

    Возможности


    ORDS позволяет:

    • Организовать доступ к ресурсам в стиле RESTful.
    • Построить взаимодействие в стиле «Вызов удаленной процедуры» (RPC-style interaction).

    Проще говоря, с помощью ORDS можно открыть доступ через HTTP к определенным объектам БД — таблицам, процедурам, функциям, пакетам.

    В первом случае имеем дело с ресурсами в том смысле, как они понимаются в архитектурном стиле RESTful. Каждый ресурс определяется уникальным URI, а операции с ресурсами (CRUD — создание, чтение, изменение, удаление) определяются операциями из HTTP-запроса: PUT, POST, GET, DELETE. Например, если ресурс — это запись в таблице сотрудников, то эта запись будет доступна по URI вида:

    GET https://server:port/ords/workspace/hr/employees/7369

    Здесь в URI указывается имя схемы БД, имя таблицы и ID записи в таблице. Таким образом, этот HTTP-запрос на самом деле выполняет операцию выбора (SELECT) из указанной таблицы. С остальными операциями (добавление, изменение, удаление) принцип общения такой же: в URI указывается путь к ресурсу, а в теле запроса — дополнительные параметры, например значения полей для вставки записи в таблицу.

    Во втором случае вместо обращения к ресурсу напрямую используется вызов процедуры, которая может делать все то же самое, что и в первом случае (CRUD), а также исполнять любую другую логику, реализующую нужный бизнес-процесс. Вызов процедуры из пакета может быть сделан с помощью HTTP-запроса такого формата:

    http://localhost:8080/ords/my_schema/my_pkg/MY_PROC

    Параметры для процедуры передаются в теле запроса в виде JSON-структур формата {"param" : "value"}. Такой же подход используется и при применении Native Oracle WS, рассмотренном выше, но в случае REST-сервисов нет ограничений, накладываемых протоколом SOAP.

    Плюсы

    • Гибкий механизм, позволяющий реализовать веб-сервисы любой сложности.
    • Простота реализации: для превращения PL/SQL-процедуры в веб-сервис нужно выполнить всего несколько типовых команд. А с использованием SQL Developer создание REST API превращается в кликанье мышкой с минимумом написания кода.
    • Возможность реализации веб-сервиса без аутентификации.

    Минус

    • ORDS не входит в стандартную поставку Oracle — его нужно устанавливать отдельно для каждого сервера (дополнительных лицензий, как уже говорилось, не требуется). Это может быть проблемой, если у вас имеются тысячи серверов БД.

    Настройка


    ORDS можно запустить в двух режимах: через сервер приложений или в автономном (standalone) режиме.

    В данной статье рассматривается только вариант запуска в standalone-режиме.

    Для работы ORDS нужно:

    1. Настроить параметры перед установкой.
    2. Выполнить установку ODRS.
    3. Запустить ORDS.
    4. Настроить ACL (для нужной схемы БД).

    Настройка параметров


    В дистрибутиве ORDS имеется файл с параметрами, который по умолчанию выглядит так:

    db.hostname=
    db.port=
    db.servicename=
    db.sid=
    db.username=APEX_PUBLIC_USER
    migrate.apex.rest=false
    rest.services.apex.add=
    rest.services.ords.add=true
    schema.tablespace.default=SYSAUX
    schema.tablespace.temp=TEMP
    standalone.http.port=8080
    standalone.static.images=
    user.tablespace.default=USERS
    user.tablespace.temp=TEMP
    

    Описание параметров можно найти в документации

    Параметры для standalone-режима:
    Параметр Описание
    db.hostname Имя или IP-адрес сервера БД Oracle
    db.port Порт БД (например, 1521)
    db.servicename Servicename базы данных
    db.username Имя пользователя PL/SQL-шлюза. По умолчанию = APEX_PUBLIC_USER, но в примере не планируется использовать APEX, поэтому можно не заполнять этот параметр
    db.password Пароль пользователя (тоже не заполняем)
    migrate.apex.rest Переход RESTful сервисов APEX на ORDS. Ставим false, если не используется APEX
    rest.services.apex.add Конфигурировать ORDS для использования в APEX
    rest.services.ords.add Установить схему БД для ORDS (ORDS_PUBLIC_USER и ORDS_METADATA). Значение должно быть true
    schema.tablespace.default Табличное пространство по умолчанию для схемы ORDS_METADATA
    schema.tablespace.temp Временное табличное пространство для схемы ORDS_METADATA
    standalone.http.port Порт, на котором будет работать ORDS. Этот порт будет использоваться в URI для доступа к WS
    user.tablespace.default Табличное пространство по умолчанию для схемы ORDS_PUBLIC_USER
    user.tablespace.temp Временное табличное пространство для схемы ORDS_PUBLIC_USER
    В итоге содержимое файла должно получиться примерно таким:

    db.hostname=your_server_host_name
    db.port=1521
    db.servicename=your_db_servicename
    migrate.apex.rest=false
    rest.services.apex.add=false
    rest.services.ords.add=true
    schema.tablespace.default=SYSAUX
    schema.tablespace.temp=TEMP
    standalone.http.port=8888
    standalone.static.images=
    user.tablespace.default=USERS
    user.tablespace.temp=TEMP

    Настройка необходимых параметров завершена, можно приступать к установке.

    Установка


    Выполняем в ОС команду:

    java -jar ords.war

    По умолчанию файл конфигурации берется из каталога params, расположенного рядом с ords.war. Можно явно указать путь к этому файлу, используя параметр --parameterFile:

    java -jar ords.war --parameterFile /path/to/params/myown_params.properties

    В диалоге установки нужно сделать следующее:

    1. Указываем путь к каталогу для сохранения конфигурации (в примере указан conf — в результате в том же каталоге, где находится файл ords.war, будет создан каталог conf, в котором будут созданы файлы с конфигурацией ORDS).
    2. После появления указания Enter the database password for ORDS_PUBLIC_USER водим пароль для схемы ORDS_PUBLIC_USER. Под этим пользователем ORDS будет подключаться к БД.
    3. После появления указания Enter 1 if you want to use PL/SQL Gateway or 2 to skip this step отвечаем «2», так как мы не собираемся использовать APEX и нам не нужно делать миграцию с mod_plsql.
    4. После появления указания Enter 1 if you wish to start in standalone mode or 2 to exit [1] отвечаем «1».
    5. После появления указания Enter 1 if using HTTP or 2 if using HTTPS [1] отвечаем 1.

    Диалог установки
    D:\ords-19.2.0.199.1647>java -jar ords.war install
    This Oracle REST Data Services instance has not yet been configured.
    Please complete the following prompts


    Enter the location to store configuration data: conf
    Enter the database password for ORDS_PUBLIC_USER:
    Confirm password:
    ёхэ 03, 2019 2:47:49 PM oracle.dbtools.rt.config.setup.SchemaSetup getInstallOrUpgrade
    WARNING: Failed to connect to user ORDS_PUBLIC_USER jdbc:oracle:thin:@//***YOUR_HOST_NAME.DOMAIN***:1521/***YOUR_SERVICE_NAME.DOMAIN***

    Enter 1 if you want to use PL/SQL Gateway or 2 to skip this step.
    If using Oracle Application Express or migrating from mod_plsql then you must enter 1 [1]:2
    ёхэ 03, 2019 2:48:32 PM
    INFO: reloaded pools: []
    Enter 1 if you wish to start in standalone mode or 2 to exit [1]:
    Enter 1 if using HTTP or 2 if using HTTPS [1]:
    2019-09-03 14:48:49.754:INFO::main: Logging initialized @4276887ms to org.eclipse.jetty.util.log.StdErrLog
    ёхэ 03, 2019 2:48:49 PM
    INFO: HTTP and HTTP/2 cleartext listening on port: 8082
    ёхэ 03, 2019 2:48:50 PM
    INFO: Disabling document root because the specified folder does not exist: D:\ords-19.2.0.199.1647\conf\ords\standalone\doc_root
    Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at oracle.dbtools.jarcl.Entrypoint.invoke(Entrypoint.java:66)
    at oracle.dbtools.jarcl.Entrypoint.main(Entrypoint.java:89)
    Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(Unknown Source)
    at java.lang.AbstractStringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at java.util.AbstractMap.toString(Unknown Source)
    at java.lang.String.valueOf(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at oracle.dbtools.jarcl.zip.ZipIndex.toString(ZipIndex.java:166)
    at java.lang.String.valueOf(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at oracle.dbtools.jarcl.JarClassLoader.toString(JarClassLoader.java:51)
    at org.eclipse.jetty.server.ClassLoaderDump.dump(ClassLoaderDump.java:67)
    at org.eclipse.jetty.util.component.Dumpable.dumpObjects(Dumpable.java:225)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.dumpObjects(ContainerLifeCycle.java:746)
    at org.eclipse.jetty.server.handler.ContextHandler.dump(ContextHandler.java:259)
    at org.eclipse.jetty.util.component.Dumpable.dumpObjects(Dumpable.java:162)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.dumpObjects(ContainerLifeCycle.java:746)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.dump(ContainerLifeCycle.java:701)
    at org.eclipse.jetty.util.component.Dumpable.dump(Dumpable.java:62)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.dump(ContainerLifeCycle.java:684)
    at oracle.dbtools.standalone.StandaloneConfiguration.start(StandaloneConfiguration.java:241)
    at oracle.dbtools.standalone.Standalone.execute(Standalone.java:508)
    at oracle.dbtools.cmdline.DefaultCommand.execute(DefaultCommand.java:137)
    at oracle.dbtools.cmdline.Commands.execute(Commands.java:207)
    at oracle.dbtools.cmdline.Commands.main(Commands.java:163)
    at oracle.dbtools.cmdline.Commands.main(Commands.java:368)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at oracle.dbtools.jarcl.Entrypoint.invoke(Entrypoint.java:66)

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

    Cм. также: Install Oracle REST Data Services 3.0.X in under 5 minutes.

    Запуск


    Запуск в автономном режиме выполняется командой:

    java -jar ords.war standalone

    Диалог запуска
    D:\ords-19.2.0.199.1647>java -jar ords.war standalone
    2019-09-03 15:52:45.825:INFO::main: Logging initialized @2079ms to org.eclipse.jetty.util.log.StdErrLog
    ёхэ 03, 2019 3:52:45 PM
    INFO: HTTP and HTTP/2 cleartext listening on port: 8082
    ёхэ 03, 2019 3:52:45 PM
    INFO: Disabling document root because the specified folder does not exist: D:\ords-19.2.0.199.1647\conf\ords\standalone\doc_root
    2019-09-03 15:52:47.124:INFO:oejs.Server:main: jetty-9.4.z-SNAPSHOT; built: 2019-05-02T09:46:34.874Z; git: 14f32d50076f2b706f41a33066eb364d8492e199; jvm 1.8.0_221-b11
    2019-09-03 15:52:47.179:INFO:oejs.session:main: DefaultSessionIdManager workerName=node0
    2019-09-03 15:52:47.179:INFO:oejs.session:main: No SessionScavenger set, using defaults
    2019-09-03 15:52:47.180:INFO:oejs.session:main: node0 Scavenging every 660000ms
    ёхэ 03, 2019 3:52:48 PM
    INFO: Configuration properties for: |apex|pu|
    db.hostname=***YOUR_HOST_NAME.DOMAIN***
    db.password=******
    db.port=1521
    db.servicename=***YOUR_SERVICE_NAME.DOMAIN***
    db.username=ORDS_PUBLIC_USER
    resource.templates.enabled=true

    ёхэ 03, 2019 3:52:48 PM
    WARNING: *** jdbc.MaxLimit in configuration |apex|pu| is using a value of 10, this setting may not be sized adequately for a production environment ***
    ёхэ 03, 2019 3:52:48 PM
    WARNING: *** jdbc.InitialLimit in configuration |apex|pu| is using a value of 3, this setting may not be sized adequately for a production environment ***
    ёхэ 03, 2019 3:52:50 PM
    INFO: Oracle REST Data Services initialized
    Oracle REST Data Services version : 19.2.0.r1991647
    Oracle REST Data Services server info: jetty/9.4.z-SNAPSHOT

    2019-09-03 15:52:50.484:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@d56d67{/ords,null,AVAILABLE}
    2019-09-03 15:52:50.658:INFO:oejs.AbstractConnector:main: Started ServerConnector@12325ad{HTTP/1.1,[http/1.1, h2c]}{0.0.
    0.0:8082}
    2019-09-03 15:52:50.659:INFO:oejs.Server:main: Started @6914ms

    После этого ORDS запущен и работает. Адрес веб-сервиса будет равен http://<имя_хоста>:/ords/…, где <имя_хоста> — имя компьютера, на котором запущен ORDS (оно не обязательно должно совпадать с именем сервера БД, то есть ORDS можно запустить на другом хосте), <pоrt> — порт, указанный в конфигурации ORDS.

    При желании можно оформить скрипт для автоматического запуска ORDS при старте ОС.

    Настройка доступа


    И последний шаг — настройка ACL. Действия аналогичны тем, что и при использовании Native Oracle WS.

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

    Пример 1: Обработчик для POST-запроса


    В качестве примера рассмотрим вариант реализации RPC-style interaction, то есть сделаем метод веб-сервиса, обработчиком которого будет пакетная процедура.

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

    Как уже упоминалось выше, поддержка ORDS включена также в SQL Developer (IDE для баз данных, разрабатываемая Oracle). В SQL Developer создание веб-сервисов доступно прямо из пунктов меню или контекстного меню.

    В этой статье примеры сделаны с помощью SQL Developer (версия 18), но также прилагается PL/SQL-код, который выполняется в БД в результате действий в IDE.

    Версия БД, на которой делались эксперименты:

    SQL> SELECT v.BANNER FROM v$version v;
     
    BANNER
    --------------------------------------------------------------------------------
    Oracle DATABASE 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
    PL/SQL Release 12.2.0.1.0 - Production
    CORE	12.2.0.1.0	Production
    TNS FOR Linux: Version 12.2.0.1.0 - Production
    NLSRTL Version 12.2.0.1.0 - Production

    Создание тестового пользователя для WS


    Для примеров нам снова понадобится новая схема — создаем ее:

    CREATE USER WS_TEST IDENTIFIED BY ws_test QUOTA 200M ON USERS;
    GRANT CREATE SESSION, RESOURCE TO ws_test;

    Настройка схемы БД


    После того, как схема создана, необходимо сделать ее доступной через ORDS.

    Для этого:

    1. В SQL Developer создаем подключение для WS_TEST.
    2. В списке подключений вызываем контекстное меню для нового подключения. Выбираем REST Services → Enable REST Services...:


    3. В диалоге ставим галку напротив Enable schema (также здесь можно изменить параметр «Псевдоним схемы» (Schema alias), если нужно, чтобы вместо имени схемы ws_test в URI отображался другой текст):


    4. Нажав «Далее», увидим PL/SQL-код, который в итоге будет выполнен в БД (и который можно написать самостоятельно, не пользуясь IDE):



    Листинг:

    BEGIN
        ORDS.ENABLE_SCHEMA( p_enabled             => TRUE
                          , p_schema              => 'WS_TEST'
                          , p_url_mapping_type    => 'BASE_PATH'
                          , p_url_mapping_pattern => 'ws_test'
                          , p_auto_rest_auth      => FALSE);
     
        COMMIT;
    END;
    /

    Создание модуля и шаблона


    Далее нужно создать модуль и шаблон для ресурса.

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

    Шаблон — конкретный веб-сервис, обслуживающий определенный набор методов для ресурса.

    В источнике:

    Resource module: An organizational unit that is used to group related resource templates.

    Resource template: An individual RESTful service that is able to service requests for some set of URIs (Universal Resource Identifiers). The set of URIs is defined by the URI Pattern of the Resource Template.


    Например, для интернет-магазина модуль можно назвать shop — в этом модуле будут объединены все API магазина. Шаблонами могут быть конкретные ресурсы, например, заказ (шаблон order), каталог товаров (шаблон item), оплата (шаблон payment) и т. д.

    Для создания модуля и шаблона нужно выполнить такие шаги:

    1. В дереве объектов открываем раздел REST Data Services, вызываем контекстное меню в разделе Modules, выбираем New module...:


    2. В диалоге создания модуля указываем имя модуля (shop), URI prefix (тоже указываем shop — ниже в примере сразу же видим, какой будет шаблон адреса веб-сервиса), ставим галку напротив Publish, жмем «Следующий >»:


    3. В следующем диалоге указываем имя шаблона, например order, и снова жмем «Следующий >».

      В последнем диалоге видим все введенные параметры модуля и шаблона. На вкладке SQL можно увидеть PL/SQL-код, который будет выполнен в БД при нажатии «Готово»:



    Для созданного модуля shop можно добавить еще шаблонов — также из дерева объектов, вызвав контекстное меню и выбрав пункт Add Template....

    Листинг кода для создания модуля и шаблона
    BEGIN
        ORDS.DEFINE_MODULE( p_module_name    => 'shop'
                          , p_base_path      => 'shop'
                          , p_items_per_page =>  25
                          , p_status         => 'PUBLISHED'
                          , p_comments       => NULL);
     
        ORDS.DEFINE_TEMPLATE( p_module_name    => 'shop'
                            , p_pattern        => 'order'
                            , p_priority       => 0
                            , p_etag_type      => 'HASH'
                            , p_etag_query     => NULL
                            , p_comments       => NULL);
     
        COMMIT;
    END;
    /

    Создание обработчика HTTP-запроса


    Теперь нам нужно создать обработчик HTTP-запроса для нашего сервиса.

    Сначала нам понадобится создать объекты БД, которые будут содержать логику обработки запроса.

    Нам потребуются таблица для логгирования и пакет, в котором будет код обработчика.

    Создаем таблицу с помощью скрипта:
    CREATE TABLE T_WS_LOG
    (
      id_log          NUMBER GENERATED ALWAYS AS IDENTITY,
      message         VARCHAR2(2000),
      request_header  VARCHAR2(2000),
      request_body    CLOB,
      response_header VARCHAR2(2000),
      response_body   CLOB,
      dtz_log         TIMESTAMP(6) WITH TIME ZONE DEFAULT SYSTIMESTAMP
    );
    COMMENT ON TABLE T_WS_LOG IS 'Лог обработки HTTP-запросов (ORDS)';
    COMMENT ON COLUMN T_WS_LOG.id_log IS 'ПК';
    COMMENT ON COLUMN T_WS_LOG.message IS 'Сообщение лога';
    COMMENT ON COLUMN T_WS_LOG.request_header IS 'Заголовок запроса';
    COMMENT ON COLUMN T_WS_LOG.request_body IS 'Тело запроса';
    COMMENT ON COLUMN T_WS_LOG.response_header IS 'Заголовок ответа';
    COMMENT ON COLUMN T_WS_LOG.response_body IS 'Тело ответа';
    COMMENT ON COLUMN T_WS_LOG.dtz_log IS 'Дата/время записи лога';
    ALTER TABLE T_WS_LOG ADD CONSTRAINT PK_T_WS_LOG PRIMARY KEY (ID_LOG) USING INDEX;

    Создаем пакет:
    CREATE OR REPLACE PACKAGE PK_ORDS_API IS
     
     
    FUNCTION blob2clob
        ( a_blob            BLOB
        , a_from_charset    VARCHAR2  := 'AMERICAN_AMERICA.AL32UTF8'
        , a_to_charset      VARCHAR2  := 'AMERICAN_AMERICA.AL32UTF8'
        ) RETURN            CLOB;
     
     
    PROCEDURE process_request
        ( a_request     CLOB
        );
     
     
    END PK_ORDS_API;
    /
     
    CREATE OR REPLACE PACKAGE BODY PK_ORDS_API IS
     
     
    FUNCTION blob2clob
        ( a_blob            BLOB
        , a_from_charset    VARCHAR2  := 'AMERICAN_AMERICA.AL32UTF8'
        , a_to_charset      VARCHAR2  := 'AMERICAN_AMERICA.AL32UTF8'
        ) RETURN            CLOB
    AS
        l_clob      CLOB;
        l_amount    NUMBER := 2000;
        l_offset    NUMBER := 1;
        l_buffer    VARCHAR2(32767);
        l_length    PLS_INTEGER := dbms_lob.getlength(a_blob);
    BEGIN
        dbms_lob.createtemporary(l_clob, TRUE);
        dbms_lob.OPEN(l_clob, dbms_lob.lob_readwrite);
        WHILE l_offset <= l_length LOOP
            l_buffer := UTL_RAW.cast_to_varchar2(UTL_RAW.convert( r            => dbms_lob.substr(a_blob, l_amount, l_offset)
                                                                , from_charset => a_from_charset
                                                                , to_charset   => a_to_charset));
            IF LENGTH(l_buffer) > 0 THEN
                dbms_lob.writeappend(l_clob, LENGTH(l_buffer), l_buffer);
            END IF;
            l_offset := l_offset + l_amount;
            EXIT WHEN l_offset > l_length;
        END LOOP;
        RETURN l_clob;
    END blob2clob;
     
     
    PROCEDURE process_request
        ( a_request     CLOB
        )
    AS
        TYPE TStringHash IS TABLE OF VARCHAR2(256) INDEX BY VARCHAR2(256);
        lh_hdr              TStringHash;
        l_hdr               VARCHAR2(256);
        l_resp              CLOB;
        l_response_status   INTEGER := 200;
        l_ccontent_type     VARCHAR2(64) := 'application/json';
        l_in_headers        VARCHAR2(32767);
    BEGIN
        -- Заголовки и прочие параметры входящего запроса
        lh_hdr('SERVER_SOFTWARE')       := OWA_UTIL.get_cgi_env('SERVER_SOFTWARE');
        lh_hdr('SERVER_NAME')           := OWA_UTIL.get_cgi_env('SERVER_NAME');
        lh_hdr('GATEWAY_INTERFACE')     := OWA_UTIL.get_cgi_env('GATEWAY_INTERFACE');
        lh_hdr('SERVER_PROTOCOL')       := OWA_UTIL.get_cgi_env('SERVER_PROTOCOL');
        lh_hdr('SERVER_PORT')           := OWA_UTIL.get_cgi_env('SERVER_PORT');
        lh_hdr('REQUEST_METHOD')        := OWA_UTIL.get_cgi_env('REQUEST_METHOD');
        lh_hdr('PATH_INFO')             := OWA_UTIL.get_cgi_env('PATH_INFO');
        lh_hdr('PATH_TRANSLATED')       := OWA_UTIL.get_cgi_env('PATH_TRANSLATED');
        lh_hdr('SCRIPT_NAME')           := OWA_UTIL.get_cgi_env('SCRIPT_NAME');
        lh_hdr('QUERY_STRING')          := OWA_UTIL.get_cgi_env('QUERY_STRING');
        lh_hdr('REMOTE_HOST')           := OWA_UTIL.get_cgi_env('REMOTE_HOST');
        lh_hdr('REMOTE_ADDR')           := OWA_UTIL.get_cgi_env('REMOTE_ADDR');
        lh_hdr('AUTH_TYPE')             := OWA_UTIL.get_cgi_env('AUTH_TYPE');
        lh_hdr('REMOTE_USER')           := OWA_UTIL.get_cgi_env('REMOTE_USER');
        lh_hdr('REMOTE_IDENT')          := OWA_UTIL.get_cgi_env('REMOTE_IDENT');
        lh_hdr('CONTENT-TYPE')          := OWA_UTIL.get_cgi_env('CONTENT-TYPE');
        lh_hdr('CONTENT-LENGTH')        := OWA_UTIL.get_cgi_env('CONTENT-LENGTH');
        lh_hdr('HTTP_ACCEPT')           := OWA_UTIL.get_cgi_env('HTTP_ACCEPT');
        lh_hdr('HTTP_ACCEPT_LANGUAGE')  := OWA_UTIL.get_cgi_env('HTTP_ACCEPT_LANGUAGE');
        lh_hdr('HTTP_USER_AGENT')       := OWA_UTIL.get_cgi_env('HTTP_USER_AGENT');
        lh_hdr('HTTP_COOKIE')           := OWA_UTIL.get_cgi_env('HTTP_COOKIE');
     
        l_hdr := lh_hdr.FIRST;
        WHILE l_hdr IS NOT NULL LOOP
            IF lh_hdr(l_hdr) IS NOT NULL THEN
                l_in_headers := l_in_headers||CHR(10)||l_hdr||': '||lh_hdr(l_hdr);
            END IF;
            l_hdr := lh_hdr.NEXT(l_hdr);
        END LOOP;
     
        l_resp := '{ "result" : "success" }';
     
        INSERT INTO t_ws_log
            ( message
            , request_header
            , request_body
            , response_header
            , response_body)
        VALUES
            ( NULL
            , l_in_headers
            , a_request
            , 'Content-Type: '||l_ccontent_type
            , l_resp
            );
     
        OWA_UTIL.STATUS_LINE(nstatus => l_response_status, bclose_header => FALSE);
        OWA_UTIL.MIME_HEADER(ccontent_type => l_ccontent_type, bclose_header => FALSE);
        OWA_UTIL.HTTP_HEADER_CLOSE();
        htp.p(l_resp);
    END process_request;
     
    END PK_ORDS_API;
    /

    Теперь для созданного шаблона нужно добавить обработчик. Добавляем обработчик для HTTP-метода POST.

    Для этого выполняем следующие шаги:

    1. Вызываем контекстное меню для шаблона, выбираем Add Handler и тут же выбираем HTTP-метод:

    2. Откроется окно для ввода параметров обработчика. На вкладке SQL Worksheet пишем PL/SQL-код, который будет вызываться для нашего шаблона при получении HTTP-запроса с методом POST. В качестве обработчика указываем скрипт, вызывающий процедуру process_request из созданного нами пакета. Обратите внимание, что в скрипте используется bind-переменная :body — это специальная переменная ORDS, которая будет содержать целиком тело запроса в бинарном виде (так как имеет тип BLOB). Полный список служебных переменных можно посмотреть здесь. В списке есть также переменная :body_text, которая сразу возвращает тело запроса в виде CLOB. Однако при боевой отладке этой технологии было выяснено, что некоторые запросы, возвращающие текстовые данные, тем не менее не воспринимаются в ORDS как текст. В таком случае переменная :body_text просто оказывается пустой, а запрос передается в БД в таком «бестелесном» виде. Надежнее использовать исходные данные из переменной :body, которую нужно преобразовать к типу CLOB самостоятельно в обработчике запроса. Итак, код нашего обработчика выглядит так:


    3. Нажимаем кнопку Save REST Handler — после этого метод готов к использованию.

      Листинг кода обработчика POST-запроса:

      DECLARE
          l_blob BLOB := :body;
      BEGIN
          PK_ORDS_API.process_request(a_request => PK_ORDS_API.blob2clob(l_blob));
      EXCEPTION
          WHEN OTHERS THEN
              OWA_UTIL.STATUS_LINE(nstatus => 500, bclose_header => FALSE);
              OWA_UTIL.MIME_HEADER(ccontent_type => 'application/json');
              htp.p('{ "result" : "error", "message" : "'||SQLERRM||'" }');
      END;

      При необходимости можно получить PL/SQL-код для создания обработчика. Для этого в контекстном меню ORDS-модуля нужно выбрать REST Definition и далее указать, куда выдать скрипт создания модуля, например в буфер обмена:


    Полученный код для создания модуля
    -- Generated by Oracle SQL Developer REST Data Services 18.1.0.095.1630
    -- Exported REST Definitions from ORDS Schema Version 18.4.0.r3541002
    -- Schema: WS_TEST   Date: Wed Oct 23 20:19:54 MSK 2019
    --
    BEGIN
      ORDS.ENABLE_SCHEMA(
          p_enabled             => TRUE,
          p_schema              => 'WS_TEST',
          p_url_mapping_type    => 'BASE_PATH',
          p_url_mapping_pattern => 'ws_test',
          p_auto_rest_auth      => TRUE);    
     
      ORDS.DEFINE_MODULE(
          p_module_name    => 'shop',
          p_base_path      => '/shop/',
          p_items_per_page =>  25,
          p_status         => 'PUBLISHED',
          p_comments       => NULL);      
      ORDS.DEFINE_TEMPLATE(
          p_module_name    => 'shop',
          p_pattern        => 'order',
          p_priority       => 0,
          p_etag_type      => 'HASH',
          p_etag_query     => NULL,
          p_comments       => NULL);
      ORDS.DEFINE_HANDLER(
          p_module_name    => 'shop',
          p_pattern        => 'order',
          p_method         => 'POST',
          p_source_type    => 'plsql/block',
          p_items_per_page =>  0,
          p_mimes_allowed  => '',
          p_comments       => NULL,
          p_source         => 
    'DECLARE
        l_blob BLOB := :body;
    BEGIN
        PK_ORDS_API.process_request(a_request => PK_ORDS_API.blob2clob(l_blob));
    EXCEPTION
        WHEN OTHERS THEN
            OWA_UTIL.STATUS_LINE(nstatus => 500, bclose_header => FALSE);
            OWA_UTIL.MIME_HEADER(ccontent_type => ''application/json'');
            htp.p(''{ "result" : "error", "message" : "''||SQLERRM||''" }'');
    END;'
          );
     
     
      COMMIT; 
    END;


    Замечание: если выполнить процедуру создания модуля ORDS.DEFINE_MODULE еще раз, то автоматически будут удалены все шаблоны этого модуля, при этом никакого предупреждения об этом не будет!

    Пример вызова


    На этом наш веб-сервис готов. Осталось проверить его работу.

    Для проверки выполняем запрос:

    POST http://****:8888/ords/ws_test/shop/order HTTP/1.1
    Accept-Encoding: gzip,deflate
    Content-Type: application/json
    Content-Length: 22
    Host: ****:8888
    Connection: Keep-Alive
    User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
     
    { "message" : "test" }

    В ответ получаем:

    HTTP/1.1 200 OK
    Date: Wed, 23 Oct 2019 16:54:53 GMT
    Content-Type: application/json
    Transfer-Encoding: chunked
     
    { "result" : "success" }

    Результат выполнения метода — запись с телом запроса и ответа в таблице логов:



    Как видим, отправленный HTTP-запрос успешно был обработан процедурой из пакета.

    Параметры из запроса


    Выше мы уже увидели результат работы простейшего веб-сервиса. Теперь немного усложним задачу, добавив дополнительные параметры в запрос.

    Есть два способа передачи параметров:

    Через URL. Параметры задаются в URL в стандартном виде:

    http://...URI...?p1=val1&p2=val2

    Через HEADER. Параметры задаются в заголовке запроса в виде p1: val1

    В ORDS в обработчике запроса параметры определяются в виде bind-переменных.

    Добавим в предыдущий пример два параметра: prm1 — параметр в URI, prm2 — параметр в заголовке запроса.

    Для обработки этих параметров допишем процедуру PK_ORDS_API.process_request: добавим параметры a_prm_uri и a_prm_hdr, в которые будут приходить значения наших параметров из запроса.

    Процедура-обработчик запроса с новыми параметрами
    PROCEDURE process_request
        ( a_request     CLOB
        , a_prm_uri     VARCHAR2 := NULL
        , a_prm_hdr     VARCHAR2 := NULL
        )
    AS
        TYPE TStringHash IS TABLE OF VARCHAR2(256) INDEX BY VARCHAR2(256);
        lh_hdr              TStringHash;
        l_hdr               VARCHAR2(256);
        l_resp              CLOB;
        l_response_status   INTEGER := 200;
        l_ccontent_type     VARCHAR2(64) := 'application/json';
        l_in_headers        VARCHAR2(32767);
    BEGIN
        -- Заголовки и прочие параметры входящего запроса
        lh_hdr('SERVER_SOFTWARE')       := OWA_UTIL.get_cgi_env('SERVER_SOFTWARE');
        lh_hdr('SERVER_NAME')           := OWA_UTIL.get_cgi_env('SERVER_NAME');
        lh_hdr('GATEWAY_INTERFACE')     := OWA_UTIL.get_cgi_env('GATEWAY_INTERFACE');
        lh_hdr('SERVER_PROTOCOL')       := OWA_UTIL.get_cgi_env('SERVER_PROTOCOL');
        lh_hdr('SERVER_PORT')           := OWA_UTIL.get_cgi_env('SERVER_PORT');
        lh_hdr('REQUEST_METHOD')        := OWA_UTIL.get_cgi_env('REQUEST_METHOD');
        lh_hdr('PATH_INFO')             := OWA_UTIL.get_cgi_env('PATH_INFO');
        lh_hdr('PATH_TRANSLATED')       := OWA_UTIL.get_cgi_env('PATH_TRANSLATED');
        lh_hdr('SCRIPT_NAME')           := OWA_UTIL.get_cgi_env('SCRIPT_NAME');
        lh_hdr('QUERY_STRING')          := OWA_UTIL.get_cgi_env('QUERY_STRING');
        lh_hdr('REMOTE_HOST')           := OWA_UTIL.get_cgi_env('REMOTE_HOST');
        lh_hdr('REMOTE_ADDR')           := OWA_UTIL.get_cgi_env('REMOTE_ADDR');
        lh_hdr('AUTH_TYPE')             := OWA_UTIL.get_cgi_env('AUTH_TYPE');
        lh_hdr('REMOTE_USER')           := OWA_UTIL.get_cgi_env('REMOTE_USER');
        lh_hdr('REMOTE_IDENT')          := OWA_UTIL.get_cgi_env('REMOTE_IDENT');
        lh_hdr('CONTENT-TYPE')          := OWA_UTIL.get_cgi_env('CONTENT-TYPE');
        lh_hdr('CONTENT-LENGTH')        := OWA_UTIL.get_cgi_env('CONTENT-LENGTH');
        lh_hdr('HTTP_ACCEPT')           := OWA_UTIL.get_cgi_env('HTTP_ACCEPT');
        lh_hdr('HTTP_ACCEPT_LANGUAGE')  := OWA_UTIL.get_cgi_env('HTTP_ACCEPT_LANGUAGE');
        lh_hdr('HTTP_USER_AGENT')       := OWA_UTIL.get_cgi_env('HTTP_USER_AGENT');
        lh_hdr('HTTP_COOKIE')           := OWA_UTIL.get_cgi_env('HTTP_COOKIE');
        lh_hdr('a_prm_uri')             := a_prm_uri;
        lh_hdr('a_prm_hdr')             := a_prm_hdr;
     
        l_hdr := lh_hdr.FIRST;
        WHILE l_hdr IS NOT NULL LOOP
            IF lh_hdr(l_hdr) IS NOT NULL THEN
                l_in_headers := l_in_headers||CHR(10)||l_hdr||': '||lh_hdr(l_hdr);
            END IF;
            l_hdr := lh_hdr.NEXT(l_hdr);
        END LOOP;
     
        l_resp := '{ "result" : "success" }';
     
        INSERT INTO t_ws_log
            ( message
            , request_header
            , request_body
            , response_header
            , response_body)
        VALUES
            ( NULL
            , l_in_headers
            , a_request
            , 'Content-Type: '||l_ccontent_type
            , l_resp);
     
        OWA_UTIL.STATUS_LINE(nstatus => l_response_status, bclose_header => FALSE);
        OWA_UTIL.MIME_HEADER(ccontent_type => l_ccontent_type, bclose_header => FALSE);
        OWA_UTIL.HTTP_HEADER_CLOSE();
        htp.p(l_resp);
    END process_request;

    Внутри процедуры просто сделаем запись в лог значений новых параметров.

    Добавляем новые параметры в обработчик POST-запроса — в виде bind-переменных :prm_uri и :prm_hdr.

    Обработчик POST-запроса запроса с новыми параметрами:

    DECLARE
        l_blob BLOB := :body;
    BEGIN
        PK_ORDS_API.process_request(a_request => PK_ORDS_API.blob2clob(l_blob), a_prm_uri => :prm_uri, a_prm_hdr => :prm_hdr);
    EXCEPTION
        WHEN OTHERS THEN
            OWA_UTIL.STATUS_LINE(nstatus => 500, bclose_header => FALSE);
            OWA_UTIL.MIME_HEADER(ccontent_type => 'application/json');
            htp.p('{ "result" : "error", "message" : "'||SQLERRM||'" }');
    END;

    В обработчике на вкладке Parameters объявляем переменные:


    В этой форме первое поле (Name) содержит имя параметра, которое ожидается в запросе, второе поле (Bind Parameter) — имя bind-переменной, которое будет указано в обработчике этого запроса.

    Выполним запрос с новыми параметрами:



    Результат — параметры из запроса сохранились в логе:


    Обратите внимание, что параметры из URI можно также достать из CGI-переменной QUERY_STRING, то есть для получения параметров не обязательно заводить bind-переменные — можно парсить их в самой процедуре-обработчике запроса.

    CGI-переменные


    При работе с HTTP в Oracle есть возможность получения значений переменных окружения, отражающих контекст HTTP-запроса. Получить значения переменных можно с помощью процедуры OWA_UTIL.get_cgi_env.

    Список доступных в PL/SQL CGI-переменных
    APEX_LISTENER_VERSION
    GATEWAY_INTERFACE
    GATEWAY_IVERSION
    HTTP_ACCEPT_ENCODING
    HTTP_HOST
    HTTP_PORT
    HTTP_USER_AGENT
    PATH_ALIAS
    PATH_INFO
    PLSQL_GATEWAY
    QUERY_STRING
    REMOTE_ADDR
    REMOTE_USER
    REQUEST_CHARSET
    REQUEST_IANA_CHARSET
    REQUEST_METHOD
    REQUEST_PROTOCOL
    REQUEST_SCHEME
    SCRIPT_NAME
    SERVER_NAME
    SERVER_PORT
    SERVER_PROTOCOL
    SERVER_SOFTWARE
    WEB_AUTHENT_PREFIX
    host
    user-agent
    CONTENT-LENGTH
    CONTENT-TYPE

    См. также: HTTP Headers (OWA_UTIL) and ORDS-Specific Bind Variables

    Пример 2: Доступ к таблице через ORDS


    В этом примере рассмотрим организацию доступа к объекту БД (к таблице) через ORDS.

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

    Чтобы сделать объект БД доступным через ORDS, нужно выполнить всего один шаг — команду ORDS.ENABLE_OBJECT. После этого к объекту можно обратиться по URI вида:

    http://<HOST>:<PORT>/ords/<SchemaAlias>/<ObjectAlias>
    .

    Создание тестовой таблицы


    Для примера создадим таблицу «Заказы».

    Скрипт создания таблицы:

    CREATE TABLE T_ORDER
    (
      id_order   NUMBER NOT NULL,
      NUM        VARCHAR2(32),
      buyer_name VARCHAR2(256),
      dt_order   DATE,
      memo       VARCHAR2(2000)
    );
     
    COMMENT ON TABLE T_ORDER IS 'Заказ';
    COMMENT ON COLUMN T_ORDER.id_order IS 'Первичный ключ';
    COMMENT ON COLUMN T_ORDER.num IS 'Номер заказа';
    COMMENT ON COLUMN T_ORDER.buyer_name IS 'ФИО покупателя';
    COMMENT ON COLUMN T_ORDER.dt_order IS 'Дата создания заказа';
    COMMENT ON COLUMN T_ORDER.memo IS 'Примечание';
    ALTER TABLE T_ORDER ADD CONSTRAINT PK_T_ORDER PRIMARY KEY (ID_ORDER) USING INDEX;

    Открытие доступа к таблице через ORDS


    1. В SQL Developer вызываем контекстное меню для требуемой таблицы, выбираем Enable REST Service....



    2. В окне настройки доступа ставим галку напротив Enable object, снимаем галку напротив Authorization required, нажимаем «Готово» (или «Следующий», чтобы посмотреть полученный PL/SQL-код):



    3. После выполнения этих действий таблица T_ORDER становится доступна через HTTP, базовый URI для обращения к ресурсу:

      http://<server>:<port>/ords/ws_test/t_order

      Листинг включения таблицы:

      DECLARE
        PRAGMA AUTONOMOUS_TRANSACTION;
      BEGIN
       
          ORDS.ENABLE_OBJECT(p_enabled => TRUE,
                             p_schema => 'WS_TEST',
                             p_object => 'T_ORDER',
                             p_object_type => 'TABLE',
                             p_object_alias => 't_order',
                             p_auto_rest_auth => FALSE);
       
          commit;
       
      END;
      /


    Создание или изменение записи


    Доступ к таблице открыт — проверяем, как можно создавать и редактировать записи в таблице через ORDS.

    Для создания записи выполняем запрос PUT.

    В документации ORDS в описании метода PUT указан такой шаблон:

    PUT http://<HOST>:<PORT>/ords/<SchemaAlias>/<ObjectAlias>/<KeyValues>

    То есть поле KeyValues (ключ записи) должно быть заполнено даже для новой создаваемой записи. В самом запросе должны быть перечислены все поля таблицы (но ключевое поле можно не добавлять).

    Запрос
    PUT http://<server>:<port>/ords/ws_test/t_order/25 HTTP/1.1
    Accept-Encoding: gzip,deflate
    Content-Type: application/json;charset=UTF-8
    Content-Length: 157
    Host: <server>:<port>
    Connection: Keep-Alive
    User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
     
    {
    	"num" : "ords-3472634",
    	"buyer_name" : "Buyer Name",
    	"dt_order" : "2019-10-25T12:00:00Z",
    	"memo" : "Тестовый заказ 1"
    }

    В ответ получаем все поля созданной только что записи:
    
    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Location: http://<server>:<port>/ords/ws_test/t_order/25
    ETag: "..."
    Transfer-Encoding: chunked
     
    {
       "id_order": 25,
       "num": "ords-3472634",
       "buyer_name": "Buyer Name",
       "dt_order": "2019-10-25T12:00:00Z",
       "memo": "Тестовый заказ 1",
       "links":    [
                {
             "rel": "self",
             "href": "http://<server>:<port>/ords/ws_test/t_order/25"
          },
                {
             "rel": "edit",
             "href": "http://<server>:<port>/ords/ws_test/t_order/25"
          },
                {
             "rel": "describedby",
             "href": "http://<server>:<port>/ords/ws_test/metadata-catalog/t_order/item"
          },
                {
             "rel": "collection",
             "href": "http://<server>:<port>/ords/ws_test/t_order/"
          }
       ]
    }

    Смотрим содержимое таблицы — появилась наша новая запись:



    Для изменения записи вызываем тот же метод PUT. Изменим примечание в нашем заказе:

    PUT http://<server>:<port>/ords/ws_test/t_order/25 HTTP/1.1
    Accept-Encoding: gzip,deflate
    Content-Type: application/json;charset=UTF-8
    Content-Length: 178
    Host: <server>:<port>
    Connection: Keep-Alive
    User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
     
    {
    	"num" : "ords-3472634",
    	"buyer_name" : "Buyer Name",
    	"dt_order" : "2019-10-25T12:00:00Z",
    	"memo" : "Тестовый заказ 1. Изменить примечание"
    }

    В ответе получим тот же JSON с параметрами измененной записи. В таблице видим, что примечание обновилось:



    Получение записей из таблицы


    Есть три режима запроса данных из таблицы:

    1. Постраничный запрос:

      GET http://<HOST>:<PORT>/ords/<SchemaAlias>/<ObjectAlias>/?offset=<Offset>&limit=<Limit>
    2. Запрос по условиям:

       GET http://<HOST>:<PORT>/ords/<SchemaAlias>/<ObjectAlias>/?q=<FilterClause>
    3. Запрос по первичному ключу:

      GET http://<HOST>:<PORT>/ords/<SchemaAlias>/<ObjectAlias>/<KeyValues>

    Для получения всех записей можно выполнить запрос без указания каких-либо параметров:

    GET http://<server>:<port>/ords/ws_test/t_order/

    Ответ
    HTTP/1.1 200 OK
    Date: Fri, 25 Oct 2019 15:39:58 GMT
    Content-Type: application/json
    ETag: "..."
    Transfer-Encoding: chunked
     
    {
       "items": [   {
          "id_order": 25,
          "num": "ords-3472634",
          "buyer_name": "Buyer Name",
          "dt_order": "2019-10-25T12:00:00Z",
          "memo": "Тестовый заказ 1. �зменить примечание",
          "links": [      {
             "rel": "self",
             "href": "http://<server>:<port>/ords/ws_test/t_order/25"
          }]
       }],
       "hasMore": false,
       "limit": 25,
       "offset": 0,
       "count": 1,
       "links":    [
                {
             "rel": "self",
             "href": "http://<server>:<port>/ords/ws_test/t_order/"
          },
                {
             "rel": "edit",
             "href": "http://<server>:<port>/ords/ws_test/t_order/"
          },
                {
             "rel": "describedby",
             "href": "http://<server>:<port>/ords/ws_test/metadata-catalog/t_order/"
          },
                {
             "rel": "first",
             "href": "http://<server>:<port>/ords/ws_test/t_order/"
          }
       ]
    }

    Как заставить Oracle добавлять кодировку в заголовок Content-Type — вопрос открытый.

    GET http://<server>:<port>/ords/ws_test/t_order/25

    Ответ
    HTTP/1.1 200 OK
    Date: Fri, 25 Oct 2019 15:44:35 GMT
    Content-Type: application/json
    ETag: "..."
    Transfer-Encoding: chunked
     
    {
       "id_order": 25,
       "num": "ords-3472634",
       "buyer_name": "Buyer Name",
       "dt_order": "2019-10-25T12:00:00Z",
       "memo": "Тестовый заказ 1. �зменить примечание",
       "links":    [
                {
             "rel": "self",
             "href": "http://<server>:<port>/ords/ws_test/t_order/25"
          },
                {
             "rel": "edit",
             "href": "http://<server>:<port>/ords/ws_test/t_order/25"
          },
                {
             "rel": "describedby",
             "href": "http://<server>:<port>/ords/ws_test/metadata-catalog/t_order/item"
          },
                {
             "rel": "collection",
             "href": "http://<server>:<port>/ords/ws_test/t_order/"
          }
       ]
    }

    Удаление записи


    Для удаления используем HTTP-метод DELETE.

    Запрос:

    DELETE http://<server>:<port>/ords/ws_test/t_order/?q={"id_order":25}

    Запрос в исходном виде:

    DELETE http://<server>:<port>/ords/ws_test/t_order/?q=%7B%22id_order%22%3A25%7D

    Ответ:

    HTTP/1.1 200 OK
    Date=Fri, 25 Oct 2019 16:23:39 GMT
    Content-Type=application/json
    Transfer-Encoding=chunked
     
    {"rowsDeleted":1}

    Выводы


    ORDS — достаточно гибкий и универсальный механизм для работы с веб-сервисами, позволяющий реализовать полноценный REST. В плане производительности сильно превосходит Native Oracle WS с его тяжелым внутренним парсингом XML. Для реализации высоконагруженной системы этот подход не подойдет: в этом случае нужен другой стек технологий — отдельный сервер приложений, проксирование запросов, использование кластерных БД и так далее. Однако для реализации систем с относительно небольшим количеством HTTP-запросов (до 10–20 в секунду) ORDS — оптимальный подход как в производительности, так и в гибкости. ORDS уступает Native Oracle WS только в плане генерации спецификации веб-сервиса: последний выдает уже полностью готовую спецификацию (WSDL), которую «как есть» можно отдавать потребителям сервиса. В ORDS также есть возможность генерации описания, но для рассмотренного в этой статье подхода с полностью универсальным сервисом (когда есть общая процедура обработки для всех сервисов) автоматическая генерация спецификации становится невозможной. Oracle сгенерирует только спецификацию верхнего уровня, а детали (модели данных) придется описывать вручную.

    Альтернативные подходы


    Java Servlet


    Этот вариант создания веб-сервисов по способу настройки похож на Native Oracle WS: тоже требует использования встроенного HTTP-сервера Oracle, а также настройки ACL (как, впрочем, и все другие способы). Но, в отличие от Native Oracle WS, этот вариант работает с чистым HTTP. Обработчики запросов в этом случае пишутся на Java и смотрят только на тип HTTP-запроса (PUT, GET, POST и так далее, хотя можно сделать и один обработчик на все типы), а логика обработки полностью остается на усмотрение разработчика. Можно передавать тело запроса «как есть» в логику БД, и там разбирать его и обрабатывать, можно часть логики оставить на стороне Java-обработчика, а из БД вызывать уже нужную процедуру в зависимости от пришедших в запросе данных.

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

    Более подробно про этот способ можно узнать в документации Oracle.

    Database Access Descriptor (PL/SQL Servlet)


    Этот вариант полностью аналогичен предыдущему, только обработчиком запроса выступает хранимая процедура PL/SQL.

    Пример URL — указывается имя пакета, имя процедуры, параметры (GET-запрос):

    GET http://<server>:<port>/servlet_plsql/pi_test.test_serv?p_path=ppp 

    В случае POST-запроса имена параметров нужно прописывать прямо в теле запроса через знак «=», что довольно неудобно, так как тип содержимого запроса (ContentType) в этом случае может быть только текстовым. Передать xml- или json-структуру можно только в таком виде:

    p_proc_param_name=<xml_data>…</xml_data>

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

    Этот подход подробно описан на сайте ORACLE-BASE.

    Заключение


    Создание веб-сервиса в Oracle — задача довольно простая, не требующая написания какого-то сверхсложного кода. При этом разработчики Oracle получают в свой арсенал довольно мощный механизм, позволяющий интегрировать разнородные системы или части систем через HTTP.

    В данной статье мы рассмотрели четыре подхода к созданию веб-сервисов.

    Native Oracle WS — устаревшая технология, которая тем не менее имеет свои плюсы: автоматически генерируемый WSDL, автоматический парсинг XML, отсутствие необходимости установки дополнительного ПО. Главный недостаток — небольшая производительность и ограничение поддерживаемых типов данных.

    ORDS — на мой взгляд, предпочтительный способ создания веб-сервисов. Достаточно гибкий и универсальный. Из неудобств этого способа можно выделить только то, что в стандартную поставку Oracle он не входит, то есть требует отдельной установки.

    Java Servlet — вполне универсальный способ, не требующий установки дополнительного ПО. Однако все нужно делать полностью вручную, поскольку нет возможности автогенерации сервисов.

    PL/SQL Servlet — наименее удачный подход. Из плюсов можно выделить то, что в этом варианте мы получаем возможность вызова хранимых процедур через HTTP без необходимости установки дополнительного ПО, а также без написания дополнительного кода на других языках: весь код пишется только на PL/SQL.

    Всем спасибо за внимание! Надеюсь материал статьи окажется полезным тем, кто так или иначе связан с продуктами Oracle и озадачен проблемами внутрисистемных и межсистемных интеграций.

    CUSTIS
    Компания

    Похожие публикации

    Комментарии 9

      0

      Крутое решение!


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


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

        +1
        Спасибо за комментарий!
        Именно расходы на приложение не оценивали, замеряли только пропускную способность веб-сервиса до (Native XML DB Web Service) и после (ORDS). С ORDS пропускная способность оказалась на порядок выше. Вообще Native XML DB Web Service часто давал большую нагрузку на CPU; тут, конечно многое зависит от внутренней логики обработки запроса, но в случае Native WS ещё получали дополнительную нагрузку на парсинг XML. После перехода на ORDS ситуация улучшилась за счёт более оптимального парсера — часть запросов была переведена на json-формат, но даже те, что остались на xml, стали работать быстрее. БД стала нагружаться больше только за счёт того, что количество запросов увеличилось (за счёт роста числа заказов, появление большего числа интеграций и т.д.), т.е. в таких же условиях Native WS вообще переставал справляться, а переход на ORDS в целом решил проблему.
          0

          А, разумно, вы же еще сэкономили в реальности на трафике запросов — теперь они не уходят из Оракла.


          Класс, спасибо!

          +1
          С другой стороны, не требуется дополнительный сервер для http сервисов, что может помочь экономии.

          Экономия и oracle в разных углах ринга. Кто хочет экономить, не берет oracle в принципе.
            0
            Ценник конечно бывает просто заоблачным.

            Но иногда нет ничего дороже простоя, даже кратковременного.

            Хотя экономия на http сервере, это на спичках.
          0
          Однажды мы выставляли веб-сервис на джава 6, который хостился в СУБД. Все получилось очень хорошо, кроме муторной загрузки и перезагрузки пакета в БД. Скорость получилась в несколько раз быстрее, чем PL/SQL.

          Из нюансов, которые обнаружили — там кооперативная многозадачность под капотом джава-тредов (своя реализация Aurora JVM), которая не дает выигрыша в случае использования многопоточности в джаве, поэтому необходимо использовать параллельные сессии БД.
            0
            Спасибо за информацию, буду иметь в виду. Тоже смотрел сначала в сторону реализации WS через java, но не удалось обойти обязательную авторизацию, поэтому этот способ не прижился.
            0
            Замечательная статья. Прочитал вашу заметку про настройку ORDS.

            Не знаете как настроите это в бесшумном режиме?
            community.oracle.com/thread/4331921ечательная статья.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое