2024年3月28日木曜日

ページの再ポストを防止する仕組みについて

Oracle APEXのアプリケーションに含まれるページのプロパティに、ページの重複送信の許可があります。選択できる値は、はい - ページの再ポストを許可するまたは、いいえ - ページの再ポストを防止する、です。

この設定はページ・プロパティ詳細に含まれます。デフォルトは、はい - ページの再ポストを許可する、です。


ページの再ポストを防止する仕組みは、以下のように実装されています。

Oracle APEXの組込み置換文字列としてAPP_UNIQUE_PAGE_IDがあります。ドキュメントには以下のように説明されています。

APP_UNIQUE_PAGE_IDは、各ページ・ビューに固有なOracle順序から生成される整数です。この数値は、アプリケーションでの重複ページの送信を防止するために使用され、その他の用途にも使用できます。

APEXが生成する全てのページに、このAPP_UNIQUE_PAGE_IDが含まれていて、ページの送信時に同時に送信されます。非表示のパラメータp_page_submission_idの値がそれになります。


ちなみに非表示のパラメータp_flow_idアプリケーションID(APP_ID)p_flow_step_idページID(APP_PAGE_ID)p_instanceセッションID(APP_SESSION)が割り当たります。これらはOracle APEXの開発が始まった当初の名前がFlowsだったことに由来しています。(英語版WikipediaのOracle Application ExpressのBackgroundを参照のこと)。

ページの重複送信の許可いいえ - ページの再ポストを防止するになっていると、APP_UNIQUE_PAGE_IDを対象としたページの送信が1度だけしか行えないように制限されます。

簡単なAPEXアプリケーションを作成して、動作を確認してみます。

そのページのAPP_UNIQUE_PAGE_IDを表示、または、p_page_submission_idに設定する値を指定するページ・アイテムP1_APP_UNIQUE_PAGE_IDを作成します。ページを送信するボタンSUBMITp_page_submission_idにページ・アイテムP1_APP_UNIQUE_PAGE_IDの値を設定するボタンREPLACEを作成します。


ページ・アイテムP1_APP_UNIQUE_PAGE_IDは、識別タイプテキスト・フィールドとします。

ソースタイプアイテムを選択し、アイテムとしてAPP_UNIQUE_PAGE_IDを指定します。セッション・ステートストレージリクエストごと(メモリーのみ)を選択します。

ページがロードする際に、APP_UNIQUE_PAGE_IDの値がページ・アイテムに設定されます。


ボタンSUBMITは、アクションとして単にページの送信を行います。


ボタンREPLACEには動的アクションを作成し、ボタンをクリックしたときに以下のJavaScriptコードを実行するようにします。

ページ・アイテムP1_APP_UNIQUE_PAGE_IDに指定されている値を、IDがpPageSubmissionIdのINPUT要素(タイプはhidden)に設定しています。

$s("pPageSubmissionId", apex.items.P1_APP_UNIQUE_PAGE_ID.value);


ページ・プロパティのページの重複送信の許可いいえ - ページの再ポストを防止するに設定します。


アプリケーションを実行します。

ボタンSUBMITをクリックすると、APP_UNIQUE_PAGE_IDは毎回更新されます。


APP_UNIQUE_PAGE_IDの値をクリップボードにコピーします。

再度、ボタンSUBMITを複数回クリックします。


現在のAPP_UNIQUE_PAGE_IDの値を送信済みのAPP_UNIQUE_PAGE_IDに置き換え、ボタンREPLACEをクリックします。


ボタンSUBMITをクリックすると再ポストの防止が働き、「このページはすでに送信されているため、再送信できません。」とエラーが表示されます。


つまり、ページ送信時のパラメータp_page_submission_id(type=hiddenのINPUT要素 - idはpPageSubmissionId)として渡されるAPP_UNIQUE_PAGE_IDが以前に送信済みであれば、再ポストの防止が設定されているページでは、エラーが発生します。

ページの送信を行なうコードapex.page.submitの内容を確認すると、ページの再描画が行われる以前に、HTTPのPOSTの応答を受け取った時点でp_page_submission_idは更新されています。


現実的な状況としては、送信したページのレスポンスを受け取る前(p_page_submission_idが変わる前)に再度ページを送信した時に、エラーが発生すると言えます。

簡単なアプリケーションですがエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/prevent-page-repost.zip

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

2024年3月26日火曜日

Apple MシリーズのMacでOracle Autonomous Database Free Container Imageを動かす

Apple MシリーズのMacでOracle Autonomous Database Free Container Imageを動かしてみました。大体はOracle Container Registryに記載されている手順に従えばコンテナは起動しますが、作業していて躓いた点を記載します。

以下のドキュメントに記載されている手順を実施します。

Oracle Autonomous Database Free Container Image Documentation
以下のMacbook Proで作業を行なっています。今となっては世代が古いM1チップです。Mシリーズの世代で動作は変わらないとは思いますが、QEMUによるIntel CPUのエミュレーションで動作させるため、CPUの世代の違いで動作が変わることが無いとは言えません。


FAQHow can I install colima and docker on machines with M1/M2 chips ?に記載されている手順に従い、docker、docker-compose、colima、qemuをインストールします。

brew install docker
brew install docker-compose
brew install colima
brew install qemu

今回の作業で使用しているバージョンは以下です。

% docker --version

Docker version 26.0.0, build 2ae903e86c

% docker-compose --version

Docker Compose version 2.26.0

% colima --version

colima version 0.6.8

% qemu-system-x86_64 -version

QEMU emulator version 8.2.1

Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

% 


FAQHow can I start Colima x86_64 Virtual Machine using Apple's new virtualization framework - Rosetta ?の手順に従って、Colimaを構成します。

ただし、Philipp Salvisbergさんが彼のブログ記事「Oracle Database 23c on a Mac with an M-Series Chip」で指摘されていますが、colima startの引数として--arch x86_64が与えられている場合、ColimaはIntel CPUのエミュレーションにQEMUを使用し、AppleのRosetta2は使用しません。そのため、--vm-type vzおよび--vz-rosettaを指定する意味は無いようです。Colimaの用途に限ればRosetta2のインストールも不要と思われます。

colimaを開始します。

cpuに4、memoryに10を与えます。

colima start --cpu 4 --memory 10 --arch x86_64

FAQHow can I start Colima x86_64 Virtual Machine with minimum memory/cpu requirements ?のNoteに「Running x86_64 arch containers can have issues translating instructions for ARM. We give higher memory to the VM to avoid such issues」と記載があり、Colimaを開始するコマンドの引数cpuに4、memoryに10が指定されています。リソースに余裕があれば、cpu数やmemoryは(特にmemoryは)増やしても良いかと思います。

% colima start --cpu 8 --memory 18 --arch x86_64


INFO[0000] starting colima                              

INFO[0000] runtime: docker                              

INFO[0001] starting ...                                  context=vm

INFO[0065] provisioning ...                              context=docker

INFO[0067] starting ...                                  context=docker

INFO[0077] done                                         

% 


あらかじめコンテナ・イメージをダウンロードしておきます。

docker pull container-registry.oracle.com/database/adb-free:latest

% docker pull container-registry.oracle.com/database/adb-free:latest

latest: Pulling from database/adb-free

66cb99a7273e: Pull complete 

0e3c46fdb0f9: Pull complete 

054518c1d5d2: Pull complete 

Digest: sha256:65641de72d0e18787bca990c452b3a13fab6ca3a6f00f6e4bd758520efd82bf4

Status: Downloaded newer image for container-registry.oracle.com/database/adb-free:latest

container-registry.oracle.com/database/adb-free:latest

% 


コンテナadb-freeを作成します。作成するAutonomous Datababaseの管理者パスワードを環境変数MYPASSに設定します。ワークロード・タイプにATPを指定します。

export MYPASS=<ADBのパスワード>

docker run -d \
-p 1521:1522 \
-p 1522:1522 \
-p 8443:8443 \
 -p 27017:27017 \
-e WORKLOAD_TYPE='ATP' \
-e WALLET_PASSWORD=$MYPASS \
-e ADMIN_PASSWORD=$MYPASS \
--cap-add SYS_ADMIN \
--device /dev/fuse \
--name adb-free \
container-registry.oracle.com/database/adb-free:latest

% export MYPASS=********

% docker run -d \

-p 1521:1522 \

-p 1522:1522 \

-p 8443:8443 \

 -p 27017:27017 \

-e WORKLOAD_TYPE='ATP' \

-e WALLET_PASSWORD=$MYPASS \

-e ADMIN_PASSWORD=$MYPASS \

--cap-add SYS_ADMIN \

--device /dev/fuse \

--name adb-free \

container-registry.oracle.com/database/adb-free:latest

b25947e90852286716d9f7fa2abca0d3e56086bad56535843c7938ddbabcd5b7

% 


構成スクリプトの実行状況をlogsコマンドで確認します。

docker logs -f adb-free

% docker logs -f adb-free

Archive:  /u01/POD1.zip

   creating: /u01/app/oracle/oradata/

   creating: /u01/app/oracle/oradata/POD1/

   creating: /u01/app/oracle/oradata/POD1/0A156A2AE0DA39A7E06377234664C7E0/

   creating: /u01/app/oracle/oradata/POD1/0A156A2AE0DA39A7E06377234664C7E0/datafile/

  inflating: /u01/app/oracle/oradata/POD1/0A156A2AE0DA39A7E06377234664C7E0/datafile/o1_mf_temp_lwbwv5fk_.tmp  

   creating: /u01/app/oracle/oradata/POD1/0A1438F87F472D38E0637723466488D2/

   creating: /u01/app/oracle/oradata/POD1/0A1438F87F472D38E0637723466488D2/datafile/

  inflating: /u01/app/oracle/oradata/POD1/0A1438F87F472D38E0637723466488D2/datafile/o1_mf_temp_lwbwv4cg_.tmp  

  inflating: /u01/app/oracle/oradata/POD1/redo01.log  

   creating: /u01/app/oracle/oradata/POD1/10E957D242B84061E0636402000AF924/

   creating: /u01/app/oracle/oradata/POD1/10E957D242B84061E0636402000AF924/datafile/


構成スクリプトの実行中にエラーが発生し、コンテナが停止することがあります。そのような場合は、docker startでコンテナを再度開始するか、またはコンテナを再作成します。

例えば、以下のように/usr/local/bin/ordsによる構成作業が中断したことがありました。

# /tmp/hs_err_pid767.log

Could not load hsdis-amd64.so; library not loadable; PrintAssembly is disabled

#

# If you would like to submit a bug report, please visit:

#   https://bugreport.java.com/bugreport/crash.jsp

#

/usr/local/bin/ords: line 222:   767 Aborted                 ${JAVA} "${APP_VM_OPTS[@]}" ${ORDS_DEBUG} -jar "${ORDS_HOME}"/${ORDS_WAR} ${ORDS_VERBOSE} "$@"


コンテナは停止しているため、docker psではリストされません。

% docker ps

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

%


docker container ls -aを実行すると、コンテナadb-freeは作成されていることが確認できます。

docker container ls -a

% docker container ls -a

CONTAINER ID   IMAGE                                                    COMMAND                   CREATED          STATUS                       PORTS     NAMES

b25947e90852   container-registry.oracle.com/database/adb-free:latest   "/bin/bash -c /u01/s…"   12 minutes ago   Exited (134) 4 minutes ago             adb-free

%


コンテナadb-freeを起動します。

docker start adb-free

% docker start adb-free

adb-free

% 


また、以下のような状況でエラーが発生し、構成スクリプトが中断したこともありました。

ORDS: Release 23.4 Production on Tue Mar 26 05:41:46 2024


Copyright (c) 2010, 2024, Oracle.


Configuration:

  /u01/ords/


Oracle REST Data Services - Non-Interactive Customer Managed ORDS for Autonomous Database

Error the Autonomous database connection failed for user: ADMIN  TNS service name: MY_ATP_low

java.sql.SQLRecoverableException: IO Error: Connection reset by peer, connect lapse 1508 ms., Authentication lapse 0 ms.


この場合は、コンテナを削除して作り直すことで対応できました。(docker rmとdocker runの再実行)。原因は不明ですが、Colimaの環境を作成した直後にadb-freeのコンテナを作成すると、コンテナの再作成が必要なエラーが発生することが多い気がします。

最終的には以下のようにORDSが起動し、コンテナの作成が完了します。

2024-03-26T06:29:05.910Z WARNING     *** jdbc.InitialLimit in configuration |default|lo| is using a value of 3, this setting may not be sized adequately for a production environment ***

2024-03-26T06:29:49.577Z INFO        


Mapped local pools from /u01/ords/databases:

  /ords/                              => default                        => VALID     



2024-03-26T06:29:50.285Z INFO        Oracle REST Data Services initialized

Oracle REST Data Services version : 23.4.0.r3461619

Oracle REST Data Services server info: jetty/10.0.18

Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 11.0.21+9-LTS-193


2024-03-26T06:31:43.906760+00:00

MY_ATP(3):Resize operation completed for file# 503, fname /u01/app/oracle/oradata/POD1/10E957D242B84061E0636402000AF924/datafile/o1_mf_temp_m04tjrwy_.dbf, old size 2048K, new size 12288K



コンテナadb-freeが起動していると、以下のURLで手元のMacからアクセスできます。

https://localhost:8443/ords/

自己署名証明書によりHTTPSにしているため、証明書のエラーが発生します。


エラーを無視して接続すると、以下のページが開きます。


これ以降はAutonomous Databaseとして操作できます。

2024年3月22日金曜日

CohereのConnectorとしてOracle REST Data ServicesのREST APIを呼び出す

Cohereが最近ベータ版として提供を開始したConnectorsという機能があります。

Introduction to Connectors
https://docs.cohere.com/docs/connectors

Coralへリクエストを送信する際に、connectorsに自分で作成したコネクタを含めることにより、LLMに外部の知識を追加することができるようになります。

Oracle REST Data ServicesにREST APIを作成し、自作コネクタとして定義してCohereのPlaygroundから呼び出してみました。以下、作業の手順を記載します。

とりあえず、Playgroundから呼び出すだけ呼び出してみました。


作成したConnectorはDashboardから確認できます。なぜか最初のConnectorはAPIからでないと作成できませんでした。1つConnectorが作成されていると、New connectorのボタンが表示されるようになります。


CohereのConnectorとして登録するORDSのREST APIを作成します。

モジュールはcohere、テンプレートはsearchとします。CohereからはPOSTメソッドが呼び出されます。

ハンドラのコードとして以下を記述します。特に何かを検索せず、リクエストにqueryとして渡される文字列をそのままtextとして返します。



認証による保護はかけません。Connectorはservice_authoauth2(認可コード・グラント)、user_access_token(パス・スルー)の3種類の認証手順を使ってConnector(今回はOracle REST Data ServicesのREST API)を呼び出すことができますが、どれもORDSのREST APIの保護と(今までの調査の範囲では)互換がなさそうです。ORDSのREST APIをCohereのConnectorとして呼び出す際のAPIの保護に関しては課題です。


以下より、CohereのConnector APIを呼び出して、Oracle REST Data ServicesのREST APIをConnectorとして追加するAPEXアプリケーションを作成します。

CohereのAPIキーがWeb資格証明COHERE_API_KEYとして登録しておきます。

認証タイプとしてHTTPヘッダーを選択します。資格証明名Authorization、 資格証明シークレットBearerで始めて空白で区切り、CohereのAPIキーを続けた文字列を設定します。


空のアプリケーションを作成します。名前Cohere Connectorsとします。


アプリケーションが作成されたら、共有コンポーネントRESTデータ・ソースを開きます。Connectorの操作はRESTデータ・ソースとして実装します。


RESTデータ・ソース作成を開始します。


RESTデータ・ソースの作成最初からを選択し、へ進みます。続く画面で、RESTデータ・ソースの一般的な設定を行います。

RESTデータ・ソース・タイプとして簡易HTTPを選択します。名前Cohere Connectors APIURLエンドポイントとして以下を設定します。

https://api.cohere.ai/v1/connectors

へ進みます。


リモート・サーバーベースURLサービスURLパスなどが自動的に設定されます。

変更は不要なので、そのままへ進みます。


ページ区切りタイプページ区切りなしです。

へ進みます。


認証が必要ですオンにし、資格証明としてCOHERE_API_KEYを選択します。

検出をクリックします。


標準のConnectorであるweb-searchとcohere-public-docsが検出されます。

RESTデータ・ソースの作成をクリックします。


RESTデータ・ソースCohere Connector APIが作成されます。

このRESTデータ・ソースを開き、Connectorの作成、更新、削除を行なう操作を追加します。


操作の追加をクリックします。


HTTPメソッドとしてPOSTデータベース操作として行の挿入を選択します。リクエスト本文テンプレートとして以下を記述します。
{  
  "name":"#NAME#",  
  "description":"#DESCRIPTION#",  
  "url":"#URL!RAW#",  
  "service_auth": {  
    "type": "#TYPE#",
    "token": "#TOKEN!RAW#"
  }
}
以上を設定し、一旦、作成をクリックします。


作成された操作POSTを再度開き、操作パラメータ本文との同期をクリックします。


Content-Typeヘッダーを操作パラメータとして追加します。

パラメータの追加をクリックします。


タイプとしてHTTPヘッダーを選択します。名前Content-Type目的入力デフォルト値application/jsonです。静的オンにし値の変更を禁止します。

パラメータの追加をクリックします。


以上でConnectorの作成を行なう操作は追加できました。

変更の適用をクリックします。


先ほど同じ手順で操作で、Connectorの更新を行なう操作を追加します。

URLパターン/:idを設定します。HTTPメソッドPATCHデータベース操作行の更新です。リクエスト本文テンプレートとして以下を記述します。
{  
  "service_auth": {  
    "type": "#TYPE#",
    "token": "#TOKEN!RAW#"
  }
}
一旦、操作PATCHを作成し、再度操作を開いた後に、本文の同期とContent-Typeヘッダーの追加を行います。


URLパターンidも、操作パラメータとして追加します。


Connectorの削除を行なう操作を追加します。

URLパターン/:idHTTPメソッドとしてDELETEデータベース操作として行の削除を選択します。

操作DELETEではリクエスト本文パラメータContent-Typeヘッダー不要です。操作パラメータURLパターンidを追加します。


以上でCohereのConnectorを操作するRESTデータ・ソースは完成です。

Connectorの一覧と操作を行なうページを、フォーム付き対話モード・レポートのページとして作成します。

ページの作成を開始し、対話モード・レポートを選択します。

レポートの名前Connectorsフォーム・ページを含めるオンにし、フォーム・ページ名Connector Detailとします。

データ・ソースとしてRESTデータ・ソースを選択し、RESTデータ・ソースとしてCohere Connector APIを選択します。

へ進みます。


主キー列1としてID (Varchar2)を選択します。

ページの作成をクリックします。


対話モード・レポートとフォームのページが作成されます。

対話モード・レポートのページはそのままで利用できますが、フォームはレポートの列と作成、更新、削除の操作が受け付けるパラメータが異なるため、調整が必要です。


ページ・デザイナでフォームのページを開きます。

ページ・アイテムとしてP3_IDP3_URLP3_NAMEP3_DESCRIPTIONP3_TYPEP3_TOKENを作成します。P3_IDタイプ非表示で作成済みですが、他のページ・アイテムは作成されていないものもあります。P3_DESCRIPTIONタイプテキスト領域、それ以外はテキスト・フィールドとして作成します。


ページ・アイテムP3_URLP3_NAMEP3_DESCRIPTIONは作成時にのみ設定可能で、更新はできません。そのため、読取り専用タイプアイテムはNULLではないを選択し、アイテムP3_IDを設定します。


Connectorの認証パラメータであるservice_authtypeは常にbearerとするため、デフォルトタイプ静的を選択し、静的値bearerを設定します。


ページ・アイテムの準備は以上で完了です。

プロセス・ビューを開きます。

デフォルトで作成されているプロセスプロセス・フォームConnector Detailでは処理が適切に行われないため、ビルド・オプションコメント・アウトします。


Connectorを作成するプロセスを、名前Create Connectorとして作成します。タイプAPIの呼出しです。

設定タイプRESTソースRESTソースとしてCohere Connector API操作POSTを選択します。

サーバー側の条件ボタン押下時CREATEを設定します。

パラメータは、ページ番号の接頭辞を除きページ・アイテムの名前と一致しているため、適切なページ・アイテムが割り当てられます。


Connectorの更新を行なうプロセスは名前Update Connectorとして作成します。

操作PATCH - /:idサーバー側の条件ボタン押下時SAVEを設定します。


Connectorの削除を行なうプロセスは名前Delete Connectorとして作成します。

操作DELETE - /:idサーバー側の条件ボタン押下時DELETEを設定します。


以上でCohereのConnectorを操作するアプリケーションは完成です。

先ほど作成したORDS REST APIをConnectorとして作成します。

名前My First Connectorとしています。URLはORDS REST APIの完全なURLを設定します。REST API自体には保護はかけられていないため、Tokenには適当な文字列を入力します。


ConnectorとしてMy First Connectorが登録されます。


今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/cohere-connectors291.zip

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