2021年12月13日月曜日

ORDSとOracle RDF Graph Serverを同居させる

 Oracle RDF Graph Serverを少し長い期間運用するため、すでにOracle REST Data Servicesを稼働させているコンピュート・インスタンスに同居させることにしました。ARMアーキテクチャのVM.Standard.A1.Flexのインスタンスがターゲットです。これもAlways Free枠で使用しています。

同居させる方法として、以下の2つの方法があるかと思います。

  1. TomcatにORDSとOracle RDF Graph Serverの両方をデプロイする。
  2. 別のサーバーにしてロード・バランサーで振り分ける。
今回は2番目の方法を採用しています。すでに実装済みのORDSは変更せずに、Oracle RDF Graph Serverをインストールします。SSLはロード・バランサにオフロードします。Oracle RDF Graphs Serverの環境構築についてはこちらの記事と同じ手順ですが、自動起動の設定も追加で行います。

ロード・バランサでリクエストをそれぞれのサーバーに振り分けるために、ルーティング・ポリシーを設定します。

Oracle RDF Graph Serverのセットアップ


Oracle RDF Graph Serverは、ユーザーoracleで動作させます。Jettyも含め、インストール先は/home/oracle以下とします。前提となる作業は以下になります。

  1. ユーザーoracleの作成
  2. Jetty 9.x(今回はjetty-distribution-9.4.44.v20210927.zip)の/home/oracleへの配置
  3. orardf-21.4.0.warの/home/oracleへの配置
  4. ディレクトリ/home/oracle/rdf-docの作成と、orardf_swagger.jsonorardf_doc_url.txtの配置
以下、実施する作業を列記します。それぞれの手順については(自動起動関連を除き)、以前の環境構築の記事で説明しています。

1. JDK1.8のインストール

ユーザーopcで実施します。Oracle REST Data ServicesはJDK17で動かしていますが、Oracle RDF Graph Serverはそれでは動かなかったので、JDK1.8をインストールします。

sudo dnf -y install jdk1.8


複数のJDKがインストールされている場合、デフォルトの/usr/bin/javaのバージョンがJDK 1.8に変わっていないか確認します。

/usr/bin/java -version

インストール前のバージョンと同じであれば、問題ありません。

2. Jetty 9.xの展開

ユーザーoracleで実施します。以下を実行します。Jettyの細かいバージョンは、Jettyのダウンロードする時期で変わってくるかと思います。

/home/oracleで実行します。

cd /home/oracle
unzip jetty-distribution-9.4.44.v20210927.zip

作成されたディレクトリjetty-distribution-9.4.44.v20210927にjettyでアクセスできるよう、ディレクトリの名称をjettyに変更します。

mv jetty-distribution-9.4.44.v20210927 jetty

3. orardf-21.4.0.warをjetty/webapps以下にコピー

Oracle RDF Graph ServerのWebアプリケーション・アーカイブをJettyにデプロイします。

cp orardf-21.4.0.war jetty/webapps/orardf.war

4. Basic認証を使ったorardf.xmlの配置

webapps以下にorardf.xmlを作成します。Basic認証を使います。

5. jetty/etc以下にrealm.propertiesを作成

ユーザー名、パスワードおよびロールの割り当てを記載したファイルrealm.propertiesを、jetty/etc以下に作成します。ユーザー名はadminで決めうちにしていますが、パスワードはそれぞれ設定します。

echo "admin=" `java -cp jetty/lib/jetty-util-9.4.44.v20210927.jar org.eclipse.jetty.util.security.Password admin パスワード |& grep CRYPT`",rdf-admin-user" > $HOME/jetty/etc/realm.properties

6. ディレクトリ/home/oracle/workspaceの作成

Oracle RDF Graph Serverの構成情報を保存するディレクトリを作成します。

mkdir /home/oracle/workspace

複数のサーバーでロード・バランスをさせる場合、最低限、このディレクトリはサーバー間で共有する必要があります。今回はそこまでの用途ではないのでロード・バランサは構成しますが、バックエンドのサーバーとしては1台だけにOracle RDF Graph Serverを実装します。

7. ポート番号の変更

ポート8080番はOracle REST Data Servicesがすでに使用しているため、Oracle RDF Graph Serverには8081番を割り当てます。jetty/start.iniの以下の部分を変更します。

## Connector port to listen on

jetty.http.port=8081


8. 起動オプションの設定

ファイル/home/oracle/.orardfrcに、以下のようにJettyの起動オプションを記述します。この後でOracle RDF Graph Serverの起動スクリプトの名称をorardfとするため、設定ファイルが.orardfrcとなっています。
export JAVA=/usr/java/jdk1.8.0_311-aarch64/bin/java
export JAVA_OPTIONS="-Doracle.rdf.workspace.dir=/home/oracle/workspace -Dfile.encoding=UTF-8"
export JETTY_HOME=/home/oracle/jetty
export JETTY_PID=/home/oracle/jetty/jetty/orardf.pid

9. firewalldの接続許可

ユーザーopcにて実施します。firewalldにて、ポート8081番への接続を許可します。

sudo firewall-cmd --add-port=8081/tcp
sudo firewall-cmd --list-all
sudo firewall-cmd --runtime-to-permanent

[opc@cmordsa1 ~]$ sudo firewall-cmd --add-port=8081/tcp

success

[opc@cmordsa1 ~]$ sudo firewall-cmd --list-all

public (active)

  target: default

  icmp-block-inversion: no

  interfaces: enp0s3

  sources: 

  services: ssh

  ports: 443/tcp 80/tcp 8081/tcp

  protocols: 

  forward: no

  masquerade: no

  forward-ports: 

port=443:proto=tcp:toport=8443:toaddr=

port=80:proto=tcp:toport=8080:toaddr=

  source-ports: 

  icmp-blocks: 

  rich rules: 

[opc@cmordsa1 ~]$ sudo firewall-cmd --runtime-to-permanent

success

[opc@cmordsa1 ~]$ 


10. gitのインストール

ユーザーopcにて実施します。Swagger UIをインストールするため、gitをインストールします。

sudo dnf -y install git

11. Swagger UIのインストール

ユーザーoracleにて実施します。GitHubにあるSwagger UIをインストールします。

cd /home/oracle
git clone https://github.com/swagger-api/swagger-ui.git

12. Swagger UIの有効化

ユーザーoracleにて実施します。コンピュート・インスタンス上の静的ファイルに外部からアクセスできるように、jetty/webapps以下にswagger-ui.xmlおよびrdf-doc.xmlを作成します。/home/opcと記載されている部分は/home/oracleに書き直します。

13. 自動起動の設定

ユーザーopcにて実施します。/etc/systemd/system/orardf.serviceを作成し、以下の内容を記載します。
[Unit]
Description=Oracle RDF Graph Server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
ExecStart=/etc/init.d/orardf start
ExecStop=/etc/init.d/orardf stop
ExecReload=/etc/init.d/orardf restart
User=oracle

[Install]
WantedBy=multi-user.target
/home/oracle/jetty/bin/jetty.sh/etc/init.d/orardfにコピーします。

sudo cp /home/oracle/jetty/bin/jetty.sh /etc/init.d/orardf
sudo chmod 755 /etc/init.d/orardf

自動起動を有効にします。

sudo systemctl enable orardf

以上で、Oracle RDF Graph Serverのインストールと構成は完了です。Oracle RDF Graph Serverを起動します。

sudo systemctl strart orardf

ロード・バランサの設定


構成済みのロード・バランサに、新たにバックエンド・セットを作成します。

ロード・バランサのリソースよりバックセンド・セットを選択し、バックエンド・セットの作成をクリックします。


ドロワーが開きます。名前orardfポート8081ステータス・コード302URLパス(URI)には/orardfを指定します。それ以外はデフォルトの設定(トラフィック分散ポリシー重み付けラウンド・ロビンSSLの使用OFF)とします。

バックエンド・セットの作成をクリックします。


バックエンド・セットが作成されたら、バックエンドの作成を行います。作成されたバックエンド・セットを開きます。


バックエンド・セットのリソースよりバックエンドを選択し、バックエンドの追加をクリックします。


ドロワーが開きます。バックエンドはコンピュート・インスタンスから選択するオプションを選び、Oracle RDF Graph Serverを実装したインスタンスにチェックを入れます。ポート8081に変更します。セキュリティ・リスト・ルールを自動的に追加しますを選択します。追加が必要なイングレス・ルールやエグレス・ルールはネットワークの構成で変わってきます。

追加をクリックします。


Oracle RDF Graph Serverが起動していれば、各種ヘルスチェックがOKに変わります。


Oracle REST Data ServicesとOracle RDF Graph Serverのバックエンド・セットが作成されました。受け付けたリクエストをぞれぞれにバックエンド・セットに振り分けるルーティング・ポリシーを作成します。

ロード・バランサのリソースルーティング・ポリシーを選択し、ルーティング・ポリシーの作成をクリックします。


新たに作成するルーティング・ポリシーの名前ADBとします。

ルール1として、Oracle RDF Graph Serverへのルーティング・ルールを設定します。

名前orardfとします。条件次の条件を満たす場合...いずれかが一致した場合を選択します。すべての条件で、条件タイプとしてパス演算子次で始まるを選択し、URL文字列として/orardf/swagger-ui/rdf-docを指定します。アクションバックエンド・セットとして、Oracle RDF Graph Serverであるorardfを選択します。

Oracle REST Data Servicesへのルーティング・ルールを作成するため、別のルールをクリックします。



Oracle REST Data Servicesのルールの名前ordsとします。次の条件を満たす場合...すべてが一致した場合(条件は一つだけなので、どちらでも結果は同じ)を選び、条件として条件タイプパス演算子次で始まるURL文字列として/ordsを指定します。アクションバックエンド・セットとしてOracle REST Data Servicesのバックエンド・セット(この例ではapexlbbs)を選択します。

に進みます。


セット順序の変更は不要なので、そのままルーティング・ポリシーの作成をクリックします。


ルーティング・ポリシーが作成されます。


作成したルーティング・ポリシーをリスナーに適用します。ロード・バランサのリソースリスナーを開き、作成済みのリスナーの編集を行います。


リスナーの編集を行うドロワーが開きます。その中のルーティング・ポリシーとして、先ほど作成したルーティング・ポリシー(今までの例ではADB)を選択します。

変更の保存をクリックします。


リスナーへのルーティング・ポリシーの適用が完了すると、すべての設定は完了です。

Oracle RDF Graph Serverにアクセスし、動作を確認します。

https://ホスト名/orardf/


Swagger UIにアクセスします。

https://ホスト名/swagger-ui/dist/
https://ホスト名/rdf-doc/orardf_swagger.json


Oracle REST Data Services(Oracle APEX)にアクセスします。(こちらの記事に記載した、外部ネットワークからの管理ツール、開発ツールのアクセスを禁止した環境であるため、サンプル・サプリケーションのサインイン画面が表示されています。)

https://ホスト名/ords/


以上で確認作業も完了です。

SSLのオフロードを行なっていますが、こちらの記事と同様のCSRF対応を行うことにより、Oracle RDF Graph ServerのREST APIの呼び出しは成功しています。

以上になります。

Oracle  APEXの環境構築の参考になれば幸いです。

2021年12月10日金曜日

Oracle RDF Graph ServerをAutonomous Databaseで使用する(5) - REST APIを呼び出す

 Oracle RDF Graph ServerはREST APIを提供しています。Autonomous Databaseから、このREST APIを呼び出してみます。

今までのところOracle RDF Graph Serverでは、認証にorg.eclipse.jetty.security.authentication.FormAuthenticatorを使っています。REST APIを保護するには使いにくい(フォームに対してユーザー名/パスワードを送信する必要がある)ため、HTTPのBasic認証を使うように変更します。

jetty.homeのwebapps以下にあるorardf.xmlを変更します。authenticatorとしてorg.eclipse.jetty.security.authentication.BasicAuthenticatorを使うように変更したorardf.xmlは以下になります。

Oracle RDF Graph Serverが提供するREST APIの仕様は、ダウンロードしたアーカイブoracle-graph-webapps-xx.x.x.ziprdf-doc/orardf_swagger.jsonとして含まれています。これ以外にドキュメントはありません。REST APIのテスト実行もできるように、Oracle RDF Graph ServerよりSwagger UIを使って参照できるようにします。

Oracle RDF Graph Serverを実装したコンピュート・インスタンス上で作業を行います。Swagger UIをインストールするためにgitコマンドを使用します。未インストールであれば、gitをインストールします。

sudo dnf -y install git

[opc@rdfgs ~]$ sudo dnf -y install git

Failed to set locale, defaulting to C.UTF-8

Last metadata expiration check: 0:47:04 ago on Fri Dec 10 03:31:38 2021.

Dependencies resolved.

==========================================================================================================

 Package                     Architecture      Version                     Repository                Size

==========================================================================================================

Installing:

 git                         x86_64            2.27.0-1.el8                ol8_appstream            164 k

Installing dependencies:

 git-core                    x86_64            2.27.0-1.el8                ol8_appstream            5.7 M

 git-core-doc                noarch            2.27.0-1.el8                ol8_appstream            2.5 M

 perl-Error                  noarch            1:0.17025-2.el8             ol8_appstream             46 k

 perl-Git                    noarch            2.27.0-1.el8                ol8_appstream             78 k

 perl-TermReadKey            x86_64            2.37-7.el8                  ol8_appstream             40 k


Transaction Summary

==========================================================================================================

Install  6 Packages


Total download size: 8.5 M

Installed size: 45 M


[中略]


Installed:

  git-2.27.0-1.el8.x86_64             git-core-2.27.0-1.el8.x86_64   git-core-doc-2.27.0-1.el8.noarch    

  perl-Error-1:0.17025-2.el8.noarch   perl-Git-2.27.0-1.el8.noarch   perl-TermReadKey-2.37-7.el8.x86_64  


Complete!

[opc@rdfgs ~]$ 

Swagger UIをGitよりインストールします。ユーザーopcのホーム・ディレクトリで実行します。

git clone https://github.com/swagger-api/swagger-ui.git

[opc@rdfgs ~]$ git clone https://github.com/swagger-api/swagger-ui.git

Cloning into 'swagger-ui'...

remote: Enumerating objects: 34525, done.

remote: Counting objects: 100% (2155/2155), done.

remote: Compressing objects: 100% (842/842), done.

remote: Total 34525 (delta 1399), reused 1985 (delta 1299), pack-reused 32370

Receiving objects: 100% (34525/34525), 377.08 MiB | 5.56 MiB/s, done.

Resolving deltas: 100% (21282/21282), done.

[opc@rdfgs ~]$ 


ユーザーopcのホーム・ディレクトリ以下に/home/opc/swagger-uiというディレクトリが作成され、Swagger UIのリポジトリが(丸ごと...)コピーされます。

続けてrfc-docというディレクトリを作成し、Oracle RDF Graph Serverのアーカイブに含まれているrdf-doc以下の2つのファイル(orardf_swagger.jsonは必須)を配置します。

[opc@rdfgs ~]$ mkdir rdf-doc

ファイルの配置作業を行う。

[opc@rdfgs ~]$ ls rdf-doc

orardf_doc_url.txt  orardf_swagger.json

[opc@rdfgs ~]$ 


jetty.homeのwebapps以下に静的ファイルを参照可能にする設定を加えます。

Swagger UIを参照可能にする設定として、swagger-ui.xmlを作成します。

RDF Graph Serverが提供するREST APIの定義を参照可能にする設定として、rdf-doc.xmlを作成します。


これらのファイルを作成したのち、jetty.homeよりwebappsの内容を確認します。以下のファイル構成になります。

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ ls webapps

README.TXT  orardf.war  orardf.xml  rdf-doc.xml  swagger-ui.xml

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ 


追加した設定はすぐにJettyに反映されます。Swagger UIにアクセスしてREST APIの定義を確認します。

以下のURLにアクセスします。

https://RDF Graph Serverのホスト名/swagger-ui/dist/

Swagger UIには以下のURLを入力します。

https://RDF Graph Serverのホスト名/rdf-doc/orardf_swagger.json


Swagger UIからGETで定義されているREST APIは発行できますが、POSTについてはCSRF(Cross Site Request Forgery)の対応がなされているため、Swagger UIから実行できません。同様に、APEXからも単純なAPEX_WEB_SERVICE.MAKE_REST_REQUESTでは呼び出せません。

POSTによるREST APIの呼び出しの例として、SPARQLクエリを実行してみます。以下、PL/SQLによる記述です。祖父とその孫をリストします。


最初に簡単なREST APIを呼び出し、ORACLE_RDFSERSVER_CSRFとして返されるクッキーの値を取得します。その値を、POST送信時にHTTPヘッダーX-CSRF-Tokenとして送信します。送信する形式はapplication/jsonとなっているのですが、SPARQLの問い合わせを単純な文字列として送信します。

SQLワークショップのSQLコマンドより実行すると、実行結果として以下が返されます。

X=http://www.example.org/family/John,Y=http://www.example.org/family/Cathy X=http://www.example.org/family/John,Y=http://www.example.org/family/Jack X=http://www.example.org/family/John,Y=http://www.example.org/family/Tom X=http://www.example.org/family/John,Y=http://www.example.org/family/Cindy


実行するSPARQLに依存して取り出される列が決まるので、ポリモーフィックテーブル関数として実装すると、どのようなSPARQLであってもひとつの関数で対応できそうです。とはいえ、APEXからの利用を考えるとSPARQLの実行を、毎回RDF Graph Serverに依頼するよりは、Query cacheにある変換済みのSQLを取得して、直接データベースでSQLを実行する方がコーディングが楽でパフォーマンスも良いと思われます。

Query cacheの参照はGETリクエストなので、比較的簡単です。

SQLコマンドから実行すると以下のような結果になります。
Cache ID: d16221c6-8f48-4e08-8926-490118b3a0c6
-------------- SPARQL ----------------
PREFIX  rdf: <http: rdf-syntax-ns="" www.w3.org="">
PREFIX rdfs: <http: rdf-schema="" www.w3.org="">
PREFIX     : <http: family="" www.example.org="">

SELECT ?x ?y
WHERE {?x :grandParentOf ?y . ?x rdf:type :Male}
-------------- Transalted SQL Start ----------------
SELECT * FROM (
SELECT /*+ NO_MERGE(R) NO_SWAP_JOIN_INPUTS(R) LEADING(R V0 V1) NO_SWAP_JOIN_INPUTS(V0) NO_SWAP_JOIN_INPUTS(V1) */ V0.VNAME_PREFIX || V0.VNAME_SUFFIX AS X, V0.VALUE_ID AS X$RDFVID, V0.VNAME_PREFIX AS X$_PREFIX, V0.VNAME_SUFFIX AS X$_SUFFIX,  (CASE WHEN V0.VALUE_TYPE IS NULL THEN NULL WHEN V0.VALUE_TYPE IN ('UR','URI') THEN 'URI'
 WHEN V0.VALUE_TYPE IN ('BN', 'BLN') THEN 'BLN'
 ELSE 'LIT'
END)  AS X$RDFVTYP, V0.LONG_VALUE AS X$RDFCLOB, V0.LITERAL_TYPE AS X$RDFLTYP, V0.LANGUAGE_TYPE AS X$RDFLANG,
V1.VNAME_PREFIX || V1.VNAME_SUFFIX AS Y, V1.VALUE_ID AS Y$RDFVID, V1.VNAME_PREFIX AS Y$_PREFIX, V1.VNAME_SUFFIX AS Y$_SUFFIX,  (CASE WHEN V1.VALUE_TYPE IS NULL THEN NULL WHEN V1.VALUE_TYPE IN ('UR','URI') THEN 'URI'
 WHEN V1.VALUE_TYPE IN ('BN', 'BLN') THEN 'BLN'
 ELSE 'LIT'
END)  AS Y$RDFVTYP, V1.LONG_VALUE AS Y$RDFCLOB, V1.LITERAL_TYPE AS Y$RDFLTYP, V1.LANGUAGE_TYPE AS Y$RDFLANG,
1 AS SEM$ROWNUM
FROM (SELECT T0.START_NODE_ID AS X$RDFVID,
T0.CANON_END_NODE_ID AS Y$RDFVID,
T0.START_NODE_ID AS BGP$1
FROM "APEXDEV"."NET1#SEMV_VFAMILY" T0, "APEXDEV"."NET1#SEMV_VFAMILY" T1
WHERE T0.P_VALUE_ID = 8440289324123914894 AND
T1.P_VALUE_ID = 834132227519661324 AND
T1.CANON_END_NODE_ID = 3746347748834679532 AND
T0.START_NODE_ID = T1.START_NODE_ID) R, "APEXDEV".NET1#RDF_VALUE$ V0, "APEXDEV".NET1#RDF_VALUE$ V1
WHERE (1=1)  AND (R.X$RDFVID = V0.VALUE_ID) AND (R.Y$RDFVID = V1.VALUE_ID)
) WHERE (1=1)
-------------- Transalted SQL End ----------------

以上で、Oracle RDF Graph ServerをAutonomous Databaseで使用する方法の紹介は終了です。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2021年12月9日木曜日

Oracle RDF Graph ServerをAutonomous Databaseで使用する(4) - Jetty 9.xのSSL化

 Oracle APEXにもOracle RDF Graph Serverにも直接は関係ありませんが、色々と作業があったので記録しておきます。

SSLやホスト名がDNSに登録されていないとREST APIを呼べない、というのはAutonomous Databaseの制約です。HTTPでIPアドレス指定でも(CSRFの対応は心配ですが)Oracle RDF Graph ServerのREST APIは動作すると思います。

以下よりJetty 9.xのSSL化にあたって行った作業を記載します。

最初にコンピュート・インスタンスに割り当てられたパブリックIPに割り当てるホスト名を決めて、DNSに登録します。次にSSLのサーバー証明書の取得を行います。以前にOracle REST Data ServicesをSSL化するために実施した手順と同じです。

Google DomainsとZero SSLのサービスを使用しています。

Zero SSLより証明書をcertificate.crt、中間証明書をca_bundle.crt、秘密鍵のファイルをprivate.keyとして入手しています。ルートCAの証明書は別途取得します。Zero SSLはUSERTrust RSA Certification AuthorityがルートCAなので、https://crt.sh/?id=1199354よりPEMをダウンロードしました。このファイルはサーバー証明書の取得先で変わります。

Oracle RDF Graph Serverを実行しているコンピュート・インスタンスに接続し、jetty.homeへ移動します。

[opc@rdfgs ~]$ cd jetty-distribution-9.4.44.v20210927/

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ pwd

/home/opc/jetty-distribution-9.4.44.v20210927

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ 

今までは、Jettyの設定はstart.iniにまとまっていました。SSLの設定がしにくいので、start.d以下のファイルに、それぞれのモジュール毎に記述する方法に変更します。start.iniは削除し、demo-base/start.dをコピーします。start.dに含まれているdemo.iniは不要なので消去します。

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ cp -r demo-base/start.d start.d

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ rm start.ini

[opc@rdfgs jetty-distribution-9.4.44.v20210927]$ rm start.d/demo.ini 

/home/opc以下にsslというディレクトリを作成しcertificate.crtca_bundle.crtprivate.key、1199354.crt(ルートCA証明書)の4つのファイルを配置します。

最初に証明書を1つのファイルに連結します。サーバー証明書、中間証明書、ルートCA証明書の順番で連結し、ファイルcertchain.crtを作成します。

cat certificate.crt ca_bundle.crt 1199354.crt > certchain.crt

[opc@rdfgs ssl]$ cat certificate.crt ca_bundle.crt 1199354.crt > certchain.crt 

OpenSSLを使用して証明書のファイルcertcachain.crtと秘密鍵private.keyより、PKCS#12形式のファイルを作成します。PKCS#12のファイル名はmy.p12にしています。

openssl pkcs12 -export -in certchain.crt -inkey private.key -out my.p12

ここで指定するパスワードは、次に実行するkeytoolで要求されるsource keystore passwordに与えます。

[opc@rdfgs ssl]$ openssl pkcs12 -export -inkey private.key -in certchain.crt -out my.p12

Enter Export Password: ********

Verifying - Enter Export Password: ********

[opc@rdfgs ssl]$ 

keytoolを使用してPKCS#12形式からJKS形式のキーストア・ファイルを生成します。destination keystore passwordは、この後に行うJettyのSSL設定に含めます。

keytool -importkeystore -srckeystore my.p12 -srcstoretype PKCS12 -destkeystore keystore.jks

[opc@rdfgs ssl]$ keytool -importkeystore -srckeystore my.p12 -srcstoretype PKCS12 -destkeystore keystore.jks

Importing keystore my.p12 to keystore.jks...

Enter destination keystore password:  ++++++++

Re-enter new password: ++++++++

Enter source keystore password: ********  

Entry for alias rdf.apexugj.dev successfully imported.

Import command completed:  1 entries successfully imported, 0 entries failed or cancelled


Warning:

The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.jks -deststoretype pkcs12".

[opc@rdfgs ssl]$ 

作成したkeystore.jksをjetty.homeのetc以下に移動します。

mv keystore.jks /home/opc/jetty-distribution-9.4.44.v20210927/etc/

[opc@rdfgs ssl]$ mv keystore.jks /home/opc/jetty-distribution-9.4.44.v20210927/etc/

ユーザーopcのホームに戻り、destination keystore passwordの難読化を行います。OBFで始まる文字列をパスワードとして使用します。

[opc@rdfgs ~]$ cd

[opc@rdfgs ~]$ java -cp jetty-distribution-9.4.44.v20210927/lib/jetty-util-9.4.44.v20210927.jar org.eclipse.jetty.util.security.Password **********

2021-12-09 06:33:46.903:INFO::main: Logging initialized @382ms to org.eclipse.jetty.util.log.StdErrLog

oracle

OBF:1v*********************x151v1x

MD5:a189c633d9995e11bf8607170ec9a4b8

[opc@rdfgs ~]$ 

${jetty.home}/start.d以下にssl.iniを作成します。内容は以下になります。

--module=ssl

jetty.ssl.host=0.0.0.0
jetty.ssl.port=8443
jetty.https.port=8443
jetty.httpConfig.securePort=443
jetty.sslContext.keyStoreType=JKS
jetty.sslContext.keyStorePath=etc/keystore.jks
jetty.sslContext.trustStorePath=etc/keystore.jks
jetty.sslContext.keyStorePassword=OBF:1v2h****************151v1x
jetty.sslContext.keyManagerPassword=OBF:1v2h****************151v1x
jetty.sslContext.trustStorePassword=OBF:1v2h****************151v1x

以上でJettyの設定は完了です。

続いてfirewalldの設定を行います。443番ポートで接続の待ち受けを行うので、接続許可を与えます。443番ポートへの接続はJettyが動作しているポート8443番に転送します。最後に設定を永続化します。

firewall-cmd --add-port=443/tcp
firewall-cmd --add-forward-port=port=443:proto=tcp:toport=8443
firewall-cmd --runtime-to-permanent

これらの手順は、Oracle RDF Graph Serverの環境構築の際に8080番ポートを対象に実施した作業と同じです。

[opc@rdfgs bin]$ sudo firewall-cmd --add-port=443/tcp

success

[opc@rdfgs bin]$ sudo firewall-cmd --add-forward-port=port=443:proto=tcp:toport=8443

success

[opc@rdfgs bin]$ sudo firewall-cmd --runtime-to-permanent

success

[opc@rdfgs bin]$ sudo firewall-cmd --list-all

public (active)

  target: default

  icmp-block-inversion: no

  interfaces: ens3

  sources: 

  services: ssh

  ports: 8080/tcp 443/tcp

  protocols: 

  masquerade: no

  forward-ports: 

port=443:proto=tcp:toport=8443:toaddr=

  source-ports: 

  icmp-blocks: 

  rich rules: 

[opc@rdfgs bin]$ 

パブリック・ネットワークのセキュリティ・リストに、443番ポートへの通信を許可するイングレス・ルールを作成します。


以上でJettyのSSL化は完了です。設定を反映させるため、Jettyを再起動します。

動作を確認します。Oracle APEXのSQLコマンドより、以下のコードを実行します。
declare
    l_clob clob;
    l_url varchar2(400);
begin
    l_url := 'https://ホスト名/orardf/api/v1/utils/user';
    l_clob := apex_web_service.make_rest_request(
        p_url => l_url
        , p_http_method => 'GET'
        , p_username => 'admin'
        , p_password => 'admin'
    );
    dbms_output.put_line(l_clob);
end;
引数p_usernameに与えた名前が応答として返されます。以下では{"userName":"admin"}が返されています。


SSLでの暗号化/復号化はロード・バランサで行い、JettyではHTTPで通信するという構成も可能です。最近、Oracle Cloud Infrastructureに証明書のサービスが追加されたため、証明書のローテーションなどを自力で行う必要が無くなりました。

ロード・バランサの作成時にSSL証明書証明書リソースとして、証明書サービス管理対象証明書を選択します。


証明書のサービスを使って、証明書のライフサイクルを管理します。


本番環境の場合は証明書サービスの利便性を考慮して、SSLはロード・バランサにオフロードさせる方が良いかもしれません。

WPG_DOCLOAD.DOWNLOAD_FILEの代わりにAPEX_DATA_EXPORT.DOWNLOADを使用する

 Oracle Databaseが提供しているWPG_DOCLOAD.DOWNLOAD_FILEを、Oracle APEXが提供しているAPEX_DATA_EXPORT.DOWNLOADに置き換えてみます。両方ともマニュアルに記載があるパッケージなので、どちらを利用しても良いでしょう。

テキストとして

吾輩は猫である。
名前はまだ無い。

をダウンロードするコーディングを行います。

APEX_DATA_EXPORT.DOWNLOADでは以下の書き方になります。

declare
    l_clob clob;
    l_download apex_data_export.t_export;
begin
    l_clob := 
q'~吾輩は猫である。
名前はまだ無い。
~';
    l_download.file_name := 'mytest';
    l_download.format := 'TXT';
    l_download.mime_type := 'text/plain';
    l_download.as_clob := TRUE;
    l_download.content_clob := l_clob;
    apex_data_export.download( p_export => l_download );
    apex_application.stop_apex_engine;
end;

WPG_DOCLOAD.DOWNLOAD_FILEでは以下の書き方になります。

declare
    l_blob blob;
    l_clob clob;
begin
    l_clob := 
q'~吾輩は猫である。
名前はまだ無い。
~';
    l_blob := wwv_flow_utilities.clob_to_blob(
        p_clob => l_clob
        , p_charset => 'AL32UTF8'
        , p_include_bom => FALSE
    );
    sys.htp.init;
    sys.htp.p('Content-Length: ' || dbms_lob.getlength(l_blob));
    sys.htp.p('Content-Type: text/plain');
    sys.htp.p('Content-Disposition: attachment; filename=mytest.txt');
    sys.owa_util.http_header_close;
    sys.wpg_docload.download_file(l_blob);
    apex_application.stop_apex_engine;
end;

上記のプロシージャをAjaxプロセスとして呼び出すAPEXアプリケーションのエクスポートを以下に置きました。

https://github.com/ujnak/apexapps/blob/master/exports/file-download-api.sql

以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

Oracle RDF Graph ServerをAutonomous Databaseで使用する(3) - SQL変換

 Oracle RDF Graph Serverを使って、マニュアルに記載されているAutonomous Databaseの制限を回避しようと試みました。こちらの記事で行っている作業です。マニュアルに載っている家系の情報から、子および孫を検索するSPARQLの文をSQLに変換します。そして、そのSQLとバインド変数を使って子および孫を検索する、というものです。

以下のように、対応できた作業と出来なかった作業があります。

  1. 追加したルールセットを含めたEntailmentが作成できない
  2. SPARQLのルールベース指定は仮想モデルで対応する

行った作業について記録しておきます。

Autonomous DatabaseでのOracle Graphの制限についての記載は以下です。

Using Oracle Autonomous Database on Shared Exadata Infrastructure

13 Using Oracle Graph with Autonomous Database
Oracle Graph Limitations with Autonomous Database

To ensure the security and performance of your database, some Oracle Graph features are restricted:

  • SPARQL editor for SQL Developer desktop is not supported
  • SEM_MATCH (SPARQL in SQL API), SEM_APIS.UPDATE_MODEL and SEM_APIS.SPARQL_to_SQL are not supported
  • Adapter for Eclipse RDF4J is not supported
  • Adapter for Apache Jena is not supported

The recommended architecture for using RDF Graphs with Autonomous Database is deployment of Oracle RDF Server and Query UI from OCI Marketplace to run SPARQL queries and other RDF data management operations.



追加したルールセットを含めたEntailmentが作成できない


RDF Graph ServerのDataのページより、Regular modelsとしてFAMILYを作成します。


作成したモデルFAMILYOpenし、SPARQLUpdateを実行します。

最初にスキーマを定義するスクリプトを実行します。


続いてインスタンスを登録するスクリプトを実行します。




以上でモデルの準備はできました。

新規にルールベースを作成します。Rulebasesで右クリックをしメニューを表示させ、Createを実行します。


Rulebaseとしてfamily_rbを入力し、OKをクリックします。これでfamily_rbというルールベースが作成されます。


Rulebasesを開き、新たに作成されたFAMILY_RBを選択します。右クリックでメニューを表示させ、Openを実行します。


FAMILY_RBRulesを選択し、Createをクリックします。


Rule Editorが開くので以下を入力し、OKをクリックします。

Name: grandparent_rule
Antecedents: (?x :parentOf ?y) (?y :parentOf ?z)
Filter: 指定なし
Consequents: (?x :grandParentOf ?z)
Aliases: SEM_ALIASES(SEM_ALIAS('','http://www.example.org/family/'))


ルールベースFAMILY_RBにルールgrandparent_ruleが作成されました。

Entailmentを作成します。

Entailments上で右クリックをしてメニューを表示させます。Createを実行します。


最初にGeneralな情報としてEntailment(の名前)にrdfs_rix_familyを入力します。NextをクリックしてModelsの指定に移動します。


モデルとしてFAMILYを選択します。SelectedONにし、NextをクリックしてRulebasesへ移動します。


RulebasesよりFAMILY_RBを選択します。


下にスクロールして、RDFSを選択します。NextをクリックしてSummaryへ移ります。


Summaryの画面でOKをクリックします。


以下のエラーが発生します。

ORA-13199: JAVAVM is not installed


これまでのGUIでの操作は、API呼び出しとしては以下と等価です。
begin
    sem_apis.create_entailment(
        index_name_in => 'rdfs_rix_family'
        , models_in => SEM_Models('FAMILY')
        , rulebases_in => SEM_Rulebases('RDFS','FAMILY_RB')
        , network_owner=>'APEXDEV'
        , network_name=>'NET1'
    );
end;
/
どちらでも同じエラーが発生します。Autonomous Databaseではデータベース内でJavaは実行出来ないようになっています。そのため、これはマニュアルに制限事項と明記されていませんが、製品の制限です。

Entailment自体は作成されます。RDFS_RIX_FAMILYOpenして確認すると、STATUSFAILEDになっています。


あくまでAutonomous Databaseの制限なので、オンプレのOracle DatabaseなどであればGUIよりEntailmentを作成できるでしょう。また、カスタムのルールセットを含まない場合(この例ではFAMILY_RBを含まない)は、Entailmentの作成は成功します。


SPARQLのルールベース指定は仮想モデルで対応する



例えばSEM_MATCH関数を使った以下のSQLがあります。


Oracle RDF Graph Serverが提供しているSPARQL Queryを実行する画面は以下です。


SEM_MATCH関数の引数rulebasesにあたる、適用するルールベースの指定が画面から見つかりません。

これは仮想モデル(Virtual Model)を作成することで対応できます。

Autonomous Databaseではカスタムのルールベースを含んだEntailmentの作成ができないので、こちらの記事にある手順でRDFネットワークをData Pumpを使ってインポートしました。インポートしたデータではなぜか作成済みのモデルがRegular Modelsに表示されません。そのため、SEM_APIS.CREATE_VIRTUAL_MODELを呼び出して仮想モデルを作成します。

仮想モデルの名前をVFAMILY、ルールベースかEntailmentのどちらかにより適用される推論ルールを指定します。以下ではEntailmentとしてRDFS_RIX_FAMILYを指定しています。
begin
    sem_apis.create_virtual_model(
        vm_name => 'VFAMILY'
        , models => sem_models('FAMILY')
        , rulebases => null
        , options => 'REPLACE=T'
        , entailments => sem_entailments('RDFS_RIX_FAMILY')
        , network_owner => 'APEXDEV'
        , network_name => 'NET1'
    );
end;
/

Regular modelsからモデルが参照できれば、GUIから仮想モデルの作成が可能です。その場合の手順は以下になります。

Virtual Modelsを選択し右クリックでメニューを表示させ、Createを実行します。


ダイアログが開くので、作成するVirtual model(の名前)としてVFAMILYを入力します。Nextをクリックします。


モデルの指定を行います。本来であれば、ここでモデルFAMILYを選択し、Nextをクリックします。


ルールベースとしてFAMILY_RBと(スクリーンショットには表示されていませんが、下にスクロールして)RDFSを選択します。Nextをクリックします。


確認の画面が表示されるので、OKをクリックすると仮想モデルVFAMILYが作成されます。



SPARQLをSQLに変換する


Oracle RDF Graph ServerでSPARQL QueryをSQLに変換します。仮想モデルVFAMILYをオープンします。以下の、子供を一覧するSPARQL問い合わせを実行します。




パラメータが指定されていない - Missing IN or OUT parameter at index:: 1 - とエラーが発生しますが、SPARQLがSQLに変換されていれば良いので、無視します。


Query cacheを開き、実行したSPARQL文のCache Detailsを参照します。


Cache DetailsよりSQLを参照します。このSQLはデータベースで直接実行できます。


孫を検索するSPARQL文は以下になります。こちらも同様の手順でSQLに変換できます。


この後は、RDFグラフの問合せにバインド変数を使用するの記事と同じ手順により、Oracle APEXのアプリケーションにSQLを組み込むことができました。

仮想モデルVFAMILYとOracle RDF Graph Serverを使って変換したSQLを使うように更新した、Oracle APEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/rdf-query-with-bind-variable2.sql

以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。