APEXのアプリケーションを使用してるユーザーが、ブラウザで複数のタブ、またはウィンドウを同じAPEXセッションで開いていると、データの不整合が発生することがあります。このような事態を防ぐために、新しくタブ、または、ウィンドウを開く際に新規にAPEXセッションを作成し、元のセッションのセッション・ステートを新しく開始したセッションにコピーできます。
セッション・クローニングはインスタンス単位で有効化/無効化します。有効にする場合は、インスタンス・パラメータのCLONE_SESSION_ENABLEDにYを設定します。セッション・クローニングはデフォルトで有効になっています。
説明だけでは分かりにくいので、APEXのセッション・クローニングを実装したAPEXアプリケーションを作成し、動作を確認します。
使用するAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/session-cloning-test.zip
アプリケーションのホーム・ページは、以下のようになっています。
/ords/r/apexdev/session-cloning-test/target?p2_org_app_session=114548193799634&request=APEX_CLONE_SESSION&session=114548193799634&cs=1blo0K2KZmnoQijOhBNokxvbhjROI2_SQxHETuH3NLhCyDwsEvRdhvQN1gbyEFSl4638mIAbCgWpnTH_7B8xwDA
ボタンRedirectをクリックすると、新規のタブを上記のURLで開きます。
CloneにDisableを指定してRedirectをクリックします。
新しいタブにTargetのページが開きます。このとき、呼び出し元のセッションID(Original Session ID)と新しいタブでのセッションID(Session ID)は、同じ値になります。
CloneをEnableに設定しボタンRedirectをクリックすると、呼び出し元のセッションID(Original Session ID)と新しいタブでのセッションID(Session ID)は、異なる値になります。つまり、新規にセッションが作成されています。
セッション・クローニングのEnable/Disableの選択は、ページ・アイテムP1_CLONEとして作成しています。
設定の選択時のページ・アクションに値のリダイレクトと設定を選択し、Enable/Disableの選択を切り替えたときにページを再ロードしています。
LOVのNULL値の表示をオン、NULL表示値にDisableを設定しています。
LOVのタイプに静的値を選び、静的値の表示値にEnable、戻り値にAPEX_CLONE_SESSIONを設定しています。
結果としてページ・アイテムP1_CLONEの値は、Disableを選択すると空白、Enableを選択するとAPEX_CLONE_SESSIONになります。
計算のPL/SQLファンクション本体として、以下を記述します。APEX_PAGE.GET_URLの引数p_requestにページ・アイテムP1_CLONEの値を渡すことにより、セッション・クローニングを有効にするか無効にするかを決めています。
declare
l_url varchar2(400);
begin
return apex_page.get_url(
p_page => 'target'
,p_items => 'P2_ORG_APP_SESSION'
,p_values => :APP_SESSION
,p_request => :P1_CLONE
);
/*
return apex_util.prepare_url(
apex_string.format('f?p=%s:target:%s:%s:::P2_ORG_APP_SESSION:%s:', :APP_ID, :APP_SESSION, :P1_CLONE, :APP_SESSION )
);
*/
end;
P1_URLに設定されたURLを新規のタブで開く処理は、ボタンREDIRECTに設定した動的アクションとして実装しています。
ボタンREDIRECTをクリックしたときに、apex.navitation.openInNewWindowを実行しています。
apex.navigation.openInNewWindow( apex.items.P1_URL.value );
セッション・クローニングの効果を確認するために、ページ・アイテムP1_COUNTを作成しています。
ソースのタイプにアイテム、アイテムとしてG_COUNTを設定し、使用のセッション・ステートの既存の値を常に置換を選択しています。
結果としてページ・アイテムP1_COUNTがページに表示される際には、常にアプリケーション・アイテムG_COUNTに保存された値が表示されます。
アイテムG_COUNTは、共有コンポーネントのアプリケーション・アイテムとして作成しています。
ボタンINCREMENTをクリックしたときに、アプリケーション・アイテムG_COUNTの値に1を加えています。
:G_COUNT := coalesce(:G_COUNT, 0) + 1;
アプリケーション・アイテムG_COUNTの値は、セッション・ステートとして保存されています。
ボタンREDIRECTをクリックしたときに開かれるページには、呼び出し元のセッションIDを表示するページ・アイテムP2_ORG_APP_SESSIONと、現行のセッションIDを表示するP2_APP_SESSIONを作成しています。
P2_APP_SESSIONのソースのタイプは静的値、静的値として&APP_SESSION.を設定しています。使用にセッション・ステートの既存の値を常に置換を選択することにより、ページ・アイテムP2_APP_SESSIONを表示する際には常に、セッション・ステートに保存されたP2_APP_SESSIONの値よりも現在のセッションIDである&APP_SESSION.を優先して表示するようにしています。
ページ・アイテムP2_COUNTのソースは、P1_COUNTと同様にアイテムG_COUNTを設定しています。
セッション・クローニングを確認するAPEXアプリケーションの説明は以上になります。
実際にセッション・クローニングが無効のときと有効のときでの、動作の違いを確認します。
アプリケーションにサインインし、何回かボタンIncrementをクリックします。
以下のスクリーンショットではCountは4になっています。
Countは4になっています。
ボタンIncrementをクリックします。Countは4,5,6,7と増えていきます。
以下のスクリーンショットではCountは7になっています。
ホーム・ページのタブに戻ります。
Countは4です。
ホーム・ページのボタンIncrementをクリックします。
Countは4から8に変わります。
これはホーム・ページとTargetのページは同じセッションで動作していて、アプリケーション・アイテムG_COUNTの値(セッション・ステート)を共有しているためです。
Targetのタブに移動すると、Countは7のままです。ここでボタンIncrementをクリックすると、Countは9になります。
セッション・クローニングを無効にして新たにタブやウィンドウを開くと、セッション・ステートが共有されるため、このような動作になります。
次にCloneをEnableにして、動作を確認します。
先ほどと同様にCountを4まで上げ、CloneにEnableを選択してボタンRedirectをクリックします。
ボタンIncrementを複数回クリックすると、Countの表示は4,5,6,7と上がっていきます。
ホーム・ページのタブに戻ります。Countの表示は変わらず4です。
ホーム・ページのボタンIncrementを複数回クリックすると、Countは5,6,7,8と増加していきます。Targetのページで変更した値の影響は受けません。これは、ホーム・ページとTargetのページでは、別のセッション・ステートを参照しているためです。
セッション・クローニングの動作の説明は以上になります。
完