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は以下になります。
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> | |
<Configure id="orardf" class="org.eclipse.jetty.webapp.WebAppContext"> | |
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> | |
<!-- Required minimal context configuration : --> | |
<!-- + contextPath --> | |
<!-- + war OR resourceBase --> | |
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> | |
<Set name="contextPath">/orardf</Set> | |
<Set name="war"><Property name="jetty.webapps" default="."/>/orardf.war</Set> | |
<Get name="securityHandler"> | |
<Set name="loginService"> | |
<New class="org.eclipse.jetty.security.HashLoginService"> | |
<Set name="name">Test Realm</Set> | |
<Set name="config"><SystemProperty name="jetty.base" default="."/>/etc/realm.properties</Set> | |
<!-- To enable reload of realm when properties change, uncomment the following lines --> | |
<!-- | |
<Set name="hotReload">true</Set> | |
<Call name="start"></Call> | |
--> | |
</New> | |
</Set> | |
<Set name="authenticator"> | |
<New class="org.eclipse.jetty.security.authentication.BasicAuthenticator"> | |
</New> | |
</Set> | |
<!-- | |
<Set name="authenticator"> | |
<New class="org.eclipse.jetty.security.authentication.FormAuthenticator"> | |
<Set name="alwaysSaveUri">true</Set> | |
</New> | |
</Set> | |
--> | |
<Set name="checkWelcomeFiles">true</Set> | |
</Get> | |
</Configure> |
Oracle RDF Graph Serverが提供するREST APIの仕様は、ダウンロードしたアーカイブoracle-graph-webapps-xx.x.x.zipにrdf-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@rdfgs ~]$ mkdir rdf-doc
ファイルの配置作業を行う。
[opc@rdfgs ~]$ ls rdf-doc
orardf_doc_url.txt orardf_swagger.json
[opc@rdfgs ~]$
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> | |
<Configure class="org.eclipse.jetty.server.handler.ContextHandler"> | |
<Set name="contextPath">/swagger-ui/dist</Set> | |
<Set name="handler"> | |
<New class="org.eclipse.jetty.server.handler.ResourceHandler"> | |
<Set name="resourceBase">/home/opc/swagger-ui/dist</Set> | |
<Set name="directoriesListed">true</Set> | |
</New> | |
</Set> | |
</Configure> |
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd"> | |
<Configure class="org.eclipse.jetty.server.handler.ContextHandler"> | |
<Set name="contextPath">/rdf-doc</Set> | |
<Set name="handler"> | |
<New class="org.eclipse.jetty.server.handler.ResourceHandler"> | |
<Set name="resourceBase">/home/opc/rdf-doc</Set> | |
<Set name="directoriesListed">false</Set> | |
</New> | |
</Set> | |
</Configure> |
[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]$
declare | |
/* 各種検索条件は定数として定義する。 */ | |
C_ORIGIN constant varchar2(40) := 'https://ホスト名'; | |
C_USERNAME constant varchar2(16) := 'RDF Graph Serverのユーザー名'; | |
C_PASSWORD constant varchar2(16) := 'RDF Graph Serverのユーザーのパスワード'; | |
C_DATASOURCE constant varchar2(16) := 'apexdev'; | |
C_DATASETDEF constant varchar2(160) := '{"metadata":[{"networkOwner":"APEXDEV","networkName":"NET1","models":["VFAMILY"]}]}'; | |
C_SPARQL constant varchar2(4000) := | |
q'~PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> | |
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> | |
PREFIX : <http://www.example.org/family/> | |
SELECT ?x ?y | |
WHERE {?x :grandParentOf ?y . ?x rdf:type :Male} | |
~'; | |
-- REST APIの呼び出しに使用する。 | |
l_blob blob; | |
l_url varchar2(400); | |
l_csrf_token varchar2(80); | |
-- SPARQLの検索結果となるJSONのパースに使用する。 | |
l_json json_object_t; | |
l_results json_object_t; | |
l_bindings json_array_t; | |
l_row json_object_t; | |
-- 以下はSPARQLで変わる。今回は ?x ?y なので X, Y が列として返される。 | |
l_x json_object_t; | |
l_y json_object_t; | |
begin | |
/* | |
* 簡単なREST APIを呼び出し、クッキーとして設定されるORACLE_RDFSERVER_CSRFの値を | |
* l_csrf_tokenとして取り出す。 | |
*/ | |
l_url := C_ORIGIN || '/orardf/api/v1/utils/user'; | |
l_blob := apex_web_service.make_rest_request_b( | |
p_url => l_url | |
, p_http_method => 'GET' | |
, p_username => C_USERNAME | |
, p_password => C_PASSWORD | |
); | |
for i in 1..apex_web_service.g_response_cookies.count | |
loop | |
if apex_web_service.g_response_cookies(i).name = 'ORACLE_RDFSERVER_CSRF' then | |
l_csrf_token := apex_web_service.g_response_cookies(i).value; | |
end if; | |
end loop; | |
-- dbms_output.put_line('ORACLE_RDFSERVER_CSRF=' || l_csrf_token); | |
/* | |
* SPARQLによる検索を実行する。 | |
*/ | |
l_url := C_ORIGIN || '/orardf/api/v1/datasets/query?datasource=' || C_DATASOURCE || '&datasetDef=' || C_DATASETDEF; | |
-- HTTPリクエスト・ヘッダーを定義する。 | |
apex_web_service.g_request_headers.delete(); | |
apex_web_service.g_request_headers(1).name := 'Content-Type'; | |
apex_web_service.g_request_headers(1).value := 'application/json'; | |
-- CSRF(Cross Site Request Forgery)の対応を行う。 | |
apex_web_service.g_request_headers(2).name := 'X-CSRF-Token'; | |
apex_web_service.g_request_headers(2).value := l_csrf_token; | |
apex_web_service.g_request_headers(3).name := 'Origin'; | |
apex_web_service.g_request_headers(3).value := C_ORIGIN; | |
apex_web_service.g_request_headers(4).name := 'Cookie'; | |
apex_web_service.g_request_headers(4).value := 'ORACLE_RDFSERVER_CSRF=' || l_csrf_token; | |
-- REST APIの呼び出し。 | |
l_blob := apex_web_service.make_rest_request_b( | |
p_url => l_url | |
, p_http_method => 'POST' | |
, p_username => C_USERNAME | |
, p_password => C_PASSWORD | |
, p_body => C_SPARQL | |
); | |
-- JSON文書から、検索結果となるそれぞれの行を取り出し印刷する。 | |
l_json := json_object_t.parse(l_blob); | |
l_results := l_json.get_object('results'); | |
l_bindings := l_results.get_array('bindings'); | |
for i in 0..(l_bindings.get_size - 1) | |
loop | |
l_row := treat(l_bindings.get(i) as json_object_t); | |
l_x := l_row.get_object('X'); | |
l_y := l_row.get_object('Y'); | |
-- XとYの値を印刷する。 | |
dbms_output.put_line('X=' || l_x.get_string('value') || ',Y=' || l_y.get_string('value')); | |
end loop; | |
end; |
declare | |
/* 各種検索条件は定数として定義する。 */ | |
C_ORIGIN constant varchar2(40) := 'https://ホスト名'; | |
C_USERNAME constant varchar2(16) := 'RDF Graph Serverのユーザー名'; | |
C_PASSWORD constant varchar2(16) := 'RDF Graph Serverのユーザーのパスワード'; | |
C_DATASOURCE constant varchar2(16) := 'apexdev'; | |
C_NETWORK_OWNER constant varchar2(16) := 'APEXDEV'; | |
C_NETWORK_NAME constant varchar2(16) := 'NET1'; | |
C_MODEL constant varchar2(16) := 'VFAMILY'; | |
-- REST APIの呼び出しに使用する。 | |
l_blob blob; | |
l_url varchar2(400); | |
l_parms apex_application_global.VC_ARR2; | |
l_values apex_application_global.VC_ARR2; | |
-- SPARQLの検索結果となるJSONのパースに使用する。 | |
l_json json_object_t; | |
l_items json_array_t; | |
l_row json_object_t; | |
begin | |
/* | |
* Query cacheの内容を取得する。 | |
*/ | |
l_url := C_ORIGIN || '/orardf/api/v1/datasources/' || C_DATASOURCE || '/sparql/cache/model'; | |
l_parms.delete; | |
l_values.delete; | |
l_parms(1) := 'networkOwner'; | |
l_values(1) := C_NETWORK_OWNER; | |
l_parms(2) := 'networkName'; | |
l_values(2) := C_NETWORK_NAME; | |
l_parms(3) := 'model'; | |
l_values(3) := C_MODEL; | |
/* REST APIの呼び出し */ | |
l_blob := apex_web_service.make_rest_request_b( | |
p_url => l_url | |
, p_http_method => 'GET' | |
, p_username => C_USERNAME | |
, p_password => C_PASSWORD | |
, p_parm_name => l_parms | |
, p_parm_value => l_values | |
); | |
-- JSON文書から、検索結果となるそれぞれの行を取り出し印刷する。 | |
l_json := json_object_t.parse(l_blob); | |
l_items := l_json.get_array('items'); | |
for i in 0..(l_items.get_size - 1) | |
loop | |
l_row := treat(l_items.get(i) as json_object_t); | |
dbms_output.put_line('Cache ID: ' || l_row.get_string('cacheId')); | |
dbms_output.put_line('-------------- SPARQL ----------------'); | |
dbms_output.put_line(l_row.get_string('sparql')); | |
dbms_output.put_line('-------------- Transalted SQL Start ----------------'); | |
dbms_output.put_line(l_row.get_string('sql')); | |
dbms_output.put_line('-------------- Transalted SQL End ----------------'); | |
end loop; | |
end; |
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 ----------------