Oracle Application Server Portal
テクニカル・ノート
AJAX を使用した高い対話性を誇る
ポートレットの構築
概要
競争の激しい今日の Web 世界では、Web アプリケーションに高機能なユーザー・ インタフェースを作成することは、もはやオプションではなく必須です。Web を 閲覧するたびに、対話性の高いユーザー・インタフェース(UI)が表示されます。 フォト Web サイトには、ドラッグ・アンド・ドロップ機能や視覚的な編集機能が 表示されるだけではなく、対話型の注文機能もあります。検索サイトでは、入力 に従って人気のある検索結果を提供します。マッピング・アプリケーションでは スムーズなスクロールが可能で、衛星写真と地図を重ねて表示することができま す。 このような高い対話性を持った Web サイトの背後には、どのようなテクノロジが あるのでしょうか。このテクノロジをポータルに適用するには、どうすればよい でしょうか。ポータルで高機能な対話を提供するには、どのようにするのが最適 でしょうか。Web アプリケーションと同様の方法で、ポートレットの使いやすさ を向上させることができるのでしょうか。 このホワイト・ペーパーでは、今日の Web サイトやポートレットで使用されてい る基礎的なテクノロジ、Ajax(Asynchronous JavaScript and XML)について説明し ます。次に、応答性の高いエンド・ユーザー・エクスペリエンスを提供するため に、ポートレットで Ajax を使用する方法を説明します。また、コンテキストに応 じてポートレットを接続するための Ajax の使用方法、つまり 1 つのポートレット から別のポートレットに情報を渡す方法も示します。 このホワイト・ペーパーの例では、標準ベースのポートレット API(JSR 168)を 使用していますが、他の Web テクノロジで作成されたポートレットにも容易に適 用できます。ここでは、Java、JSR 168 ポートレットと JavaScript の基礎を理解し ていることを前提にしています。AJAX: 画面の背後にあるテクノロジ
今日の高機能な Web アプリケーションでは、JavaScript とアプリケーション・サー バーとの非同期通信を混在させて使用しています。このメカニズムは、Ajax (Asynchronous JavaScript and XML)とも呼ばれます。Ajax は、ブラウザとアプリケーション・サーバー間で小さいデータをやり取りし、この間に、Web ページ全 体のリロードではなくページの一部をリフレッシュすることを意図しています。 Ajax には厳密に定義されたコンポーネントのセットはありませんが、次に示す主 要な Ajax の機能があります。 • JavaScript: ブラウザのスクリプト言語。 ° マウス・クリック、マウスの移動や入力など、ユーザーの対話を検出 します。
° Document Object Model(DOM)ツリーを操作することにより、動的 にページを変更します。
• XML: クライアントとサーバー間でやり取りされるデータの形式です。使 用されるデータ形式には制限はありませんが、通常は HTML が使用され ます。
AJAX の動作
従来の Web アプリケーションは HTTP 要求をブラウザから Web またはアプリケー ション・サーバーに送信します。ハイパーリンクや送信フォーム上でのマウス・ クリックなどのアクションによって要求が開始されます。サーバーは HTTP 応答 を生成し、要求されたページを返します。 Ajax Web アプリケーションは、このフローにいくつかの要素を追加します。従来 型の Web アプリケーションと同様、このアプリケーションは、ユーザー対話の結 果として要求を送信します。Ajax では、ページ全体をリフレッシュする代わりに、 ページの一部のみをリフレッシュします。 Ajax 駆動型のアプリケーションは、HTTP 要求を非同期で送信します。したがっ て、エンド・ユーザーはページ全体がロードされるまで待つことなく、そのペー ジでの対話を継続できます。Ajax エンジンは、Ajax モデルの主要部分です。Ajax エンジンは、通常は JavaScript に実装されるコードの要素で、HTML ページとともにダウンロードされブラウザ 上で実行されます。Ajax エンジンには、次のような複数の役割があります(図 1)。 1. ユーザー対話の検出。このエンジンは、ユーザー対話が実行されると、 それを検出して対応します。たとえば、ユーザーが特定の領域上にマウ スを持っていくと、Ajax エンジンはこのアクションを認識し HTTP 要求 をトリガーします。 2. サーバーへのHTTP要求の送信。事前に定義されたユーザー対話が実行 されると、Ajax エンジンは非同期で Web サーバーに要求を送信します。 3. サーバーから返されたHTTP応答の処理。エンジンは、Web サーバーま たはアプリケーション・サーバーから返されたマークアップを処理しま す。たとえば、応答が XML の場合、Ajax エンジンはそれに XSL スタイ ル・シートを適用します。 4. ページの部分的なリフレッシュの実行。このエンジンは、Document Object Model(DOM)に必要な変更を加えます。DOM は、HTML ドキュメント の内部表現であるため、レンダリングされたページが更新されます。た とえば、エンジンはサーバーから返されたデータを含む新しい HTML レ イヤーを表示できます。
図 1: Ajax Web アプリケーション・モデル
ポートレットでの AJAX
このモデルは、ポートレットではどのようにマッピングされるのでしょうか。 Web アプリケーション実装モデルは、ポートレットと同じ方法で適用できます。 従来型の Web アプリケーションの場合と同様、ここでも Ajax エンジンが主役で す。実装によっては、Ajax エンジンはポートレットを含むポータルの一部になり ますが、通常、ポートレット自体として実装されます。 Ajax エンジンがポートレットの一部である場合、ポートレット開発者やページ設 計者は、次のような疑問を解決する必要があります。 • 複数の Ajax ポートレットが同一のページ上に常駐している場合、 JavaScript ライブラリは衝突しないでしょうか。 • 同一のポートレットの複数のインスタンスがページに追加されると、ど のようになるでしょうか。 このポートレットの使用例において、Ajax エンジンの役割は、図 1 に示す Web ア プリケーション・モデルへの役割と非常によく似ています。 1. ユーザー対話の検出。ユーザーがポートレット UI と対話すると、Ajax エ ンジンは定義済みの各アクションを検出します。たとえば、ユーザーが テキスト・フィールドに文字を入力すると、Ajax エンジンはそれを追跡 し定義済のアクションを実行します。その後、入力された値が XMLHttpRequest オブジェクトのパラメータとして使用されます。 2. サーバーにHTTP要求を送信する。Ajax エンジンは、ポートレット・ア プリケーションへの要求を作成して送信します。3. サーバーから返されたHTTP応答の処理。ポートレット・アプリケーショ ンがマークアップを返すと、Ajax エンジンがそれを処理します。たとえ ば、応答が XML の場合、Ajax エンジンはそれに XSL スタイル・シート を適用して処理します。 4. ページの部分的リフレッシュの実行。ページ全体のリフレッシュを回避 して部分的なリフレッシュを支援するには、マークアップが DOM に挿入 されていることが必要です。たとえば、ポートレットから返された結果 セットは、テキスト・フィールドの真下に表示されます。 図 2: Ajax ポートレット・モデル
使用例: 対話型の電話帳ポートレット
新しいバージョンの電話帳ポートレットを作成する依頼を受けたとします。この 電話帳では、エンド・ユーザーがフルネームまたは名前の一部を入力すると、ポー トレットによって一致するすべての名前が返されます。現在のバージョンのポー トレットには、テキスト・フィールドと「送信」ボタンがあります。入力した名 前を送信するために、エンド・ユーザーは「送信」ボタンをクリックする必要が あります。ページ全体がリフレッシュされてから、ポートレットによってエンド・ ユーザーの問合せと一致する名前が表示されます。エンド・ユーザーが名前のス ペルを間違えた場合、入力をやり直し、再度「送信」ボタンをクリックする必要 があります。 Ajax ポートレット・モデルを使用した新しいバージョンのポートレットには、テ キスト・フィールドのみがあります。ユーザーが名前を入力すると、ただちに結 果がポートレットに表示されます。ポートレットは、バック・エンドへのラウン ドトリップを非同期に開始し、テキスト・ボックスに入力された文字をパラメー タとして渡します。マークアップが返されると、ポートレットはページの DOM ツリーに結果を挿入して表示します。ユーザーが入力を継続すると、その文字に 従って結果セットが絞り込まれます。図 3: コンテキストに接続された Ajax ポートレット
AJAX 電話帳ポートレットの作成
対話型の Ajax 電話帳ポートレットの作成には、何が必要でしょうか。ポートレッ トのマークアップを生成するために JSP を使用していると仮定します。次のス テップでは、前のセクションで説明したポートレットの作成方法を示します。 1. 次のコードを使用して、入力フィールドを作成します <form name="sampleForm" id="sampleForm"> <input class="portlet-form-input-field" type="text" size="14" name="custName" id="custName“ onKeyUp="assembleURL(event);" /> . . . </form> . . . <div id="results"/> 2. 入力イベントを取得します。ユーザーがキーボードのキーを押すと、 onKeyUp JavaScript イベントがトリガーされます。この部分は、Ajax エ ンジンの「ユーザー対話の監視」という役割を担います。また、返され たマークアップが挿入される場所を示す<div>タグに注意してください。 3. ポートレット・アプリケーションへのリソース URL を作成します。ポー トレット・アプリケーションには、検索操作を実行するサーブレット GetCustomerInfo が含まれています。encodeURL()メソッドは、サー ブレットへのリソース URL を作成します。注意: このポートレットに Web Services for Remote Portlets(WSRP)を介 してアクセスした場合、リソース URL により、GetCustomerInfo サー ブレットへの要求が確実にポートレットの中間層にあたるアプリケー ション・サーバーを通じてプロキシされます。
<script language="JavaScript" type="text/JavaScript"> . . . var url = '<%=response.encodeURL(request.getContextPath() + "/GetCustomerInfo")%>'; . . . 4. XMLHttpRequest オブジェクトを作成します。XMLHttpRequest オブ ジェクトの作成は、次の 1 行の JavaScript コードで行われます。
request = new XMLHttpRequest();
このコードは単純ですが、ブラウザとの互換性を確保するには、より複 雑な方法で記述する必要があります。
var request = false; try {
request = new XMLHttpRequest(); } catch (trymicrosoft) { try { request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { request = false; } } }
5. XMLHttpRequest を送信します。POST または GET の送信メソッドを定 義して、JavaScript 関数 updatePage()を指定します。これは、エンジン が応答を返してから実行されます。最後に要求を送信します。
function getCustomerInfo(url) { request.open("post", url, true);
request.onreadystatechange = updatePage; request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); request.send (createQuery(document.getElementById ("sampleForm") ) ); }
注意: フォームが送信されると、テキスト・ボックスの値がパラメータと してサーブレットに渡されます。パラメータの名前は、フィールドの名 前と同じ custName です。 6. <div>タグに応答を移入します。そのためには、返されたマークアップ を DOM ツリーの結果を表示する場所に挿入します。次のコードは、この 挿入を実行します。 function updatePage() { document.getElementById("results").innerHTML = response[0].replace(/\n/g, ""); } . . . <div id="results"> ここでこのコードは、HTML を挿入する重要な関数を強調するために簡 素化してあります。次の例のように、エラーのチェックと結果の読み取 りを処理するロジックを追加する必要があります。 function updatePage() { if (request.readyState == 4) if (request.status == 200) { var response = request.responseText.split("|"); document.getElementById ("results").innerHTML= response[0].replace(/\n/g, ""); } else if (request.status == 404) . . . // Request URL does not exist else . . . // Error } . . . <div id="results"> クライアント側のコードだけではなく、非同期要求を処理するためのサーバー側 のコンポーネントも作成する必要があります。この例では、コンポーネントはサー ブレットです。このサーブレットは String パラメータを取得します。これは、ユー ザーが入力した入力フィールドのコンテンツです。たとえば、ユーザーが文字「K」 を入力すると、このアプリケーションが文字「K」を含むパラメータを使用して サーブレットを実行します。 http://<host>:<port>/<contextRoot>/servlet/GetCustomerInfo? custName=K
サーブレットが返す結果は、次のようになります。 Kenesey Julia (910) 543-1654 Kensington Ken (488) 765-3456 Kerry Steve (542) 634-4567 Kiss Daniel (543) 543-1654 Klein Kevin (888) 343-8425 Kohen Jack (907) 838-4545 Kovacs Laszlo (431) 563-1212 ユーザーが入力を続行しているため、リストは次第に絞り込まれます。 http://<host>:<port>/<contextRoot>/servlet/GetCustomerInfo? custName=Ke Kenesey Julia (910) 543-1654 Kensington Ken (488) 765-3456 Kerry Steve (542) 634-4567 ユーザーが文字をテキスト・フィールドから削除すると、フィールドの残りのコ ンテンツがサーブレットに送信され、結果セットが更新されます。
AJAX を使用したポートレット間通信
JSR 168 仕様では、ポートレット間通信に言及していませんが、ポートレットを接 続するためのよく知られた代替策がドキュメント化されています。たとえば、ポー トレット・アプリケーションのセッション・コンテキストやベンダー固有の拡張 機能を使用して、コンテキスト型のポートレットの接続を実装できます。 しかし、従来の代替策では、高機能な対話ができませんでした。たとえば、ある ポートレットから別のポートレットに情報が渡されると、ポータル・ページ全体 がリフレッシュされます。Ajax を使用すると、ページの一部のリフレッシュを保 持しながら、ポートレット間通信を実装できます。電話帳ポートレットの例に戻 ります。ここでは、検索結果をフォームから分離して、機能を向上させます。こ の変更により、ユーザーが入力した名前が 1 つのポートレットに表示され、同時 に結果が別のポートレットに表示されます。 1. 前のセクションの説明に従って、ステップ 1∼5 を実行して電話帳ポート レットを作成します(ポートレット A)。次に、2 番目のポートレットを 作成します。 2. ポートレットB: 新しく作成したポートレットの<div>タグに、応答を移 入します。updatePage() JavaScript 関数が、返されたマークアップを Search Results ポートレットの DOM ツリーに挿入するようにしてくださ い。 ポートレット A function updatePage() { document.getElementById("results").innerHTML = response[0].replace(/\n/g, ""); }ポートレット B <div id="results"> <div>タグはポートレットB に移動したため、結果はポートレット B に 表示されます。 ポートレット A ポートレット B 図 4: コンテキストで接続された Ajax ポートレット
実行時のポートレットの接続
従来型の JSR 168 ポートレットのポートレット通信の代替策では、ポートレット 開発者がその開発時に接続の実行が必要でした。検索結果を表示するターゲッ ト・ポートレットのハード・コーディングは、一部のケースでは成功しますが、 多くの場合は実行時に接続する必要があります。 実行時の接続を実装するには、ページ設計者はポートレットが互いに「対話」で きるようにする必要があります(特にポートレットの接続の場合)。このために は、いくつかの方法があります。簡単な電話帳のシナリオでは、ポートレットに パーソナライズ機能を追加し、ポートレットが互いにリンクされパーソナライズ された情報を各ポートレットのプリファレンス・ストアに保存します。この使用例をサポートするには、両方のポートレットがそれらのマークアップを レンダリングするときに、それ自体のプリファレンス・ストアを読み取る必要が あります。最初のポートレットは、この値を使用してもう 1 つのポートレットの <div>タグ内のID を処理します。2 つのポートレットのパーソナライズされた値 が一致しない場合、非同期要求によって返された値は表示されません。 ポートレットは、PortletPreferences クラスの getValue()メソッドを使用して、 プリファレンス・ストアを読み取ります。次に読み取られた値が連結されて固定 文字列 detailAddress になります。 ポートレット A function updatePage() { document.getElementById ("detailAddress<%=prefs.getValue("linkID", "")%>"). innerHTML = response[0].replace(/\n/g, ""); } ポートレット B <div id= 'detailAddress<%=prefs.getValue("linkID", "")%>'> </div> 上述のステップでは、視覚的に相互接続されたポートレットを実装できるだけで すが、実行時に接続することもできます。
未解決の問題点
このホワイト・ペーパーでは、Ajax 対応ポートレットに関連した 2 件の一般的な 使用例を説明していますが、これ以外のシナリオも数多くあります。 開発者として、ポートレットがそれ自体の状態を保持することが必要な場合が 多々あります。ユーザーがポートレットのあるページから別のページに移動後、 最初のページに戻ってきた場合、非同期状態も含め、ポートレットが以前の状態 を復元できることが必要です。ここで取り上げた例では、ユーザーが別のページ をクリックしてから再度ポートレットを含むページに戻ってきたときに、テキス ト・フィールドには最後に入力された検索条件が含まれており、結果も表示され る必要があります。 2 つ目の未解決の問題は、同一のページ上に複数の Ajax ポートレットのインスタ ンスがあるために、JavaScript 変数とメソッド間で衝突が発生する可能性です。理 想的には、ポータル・ページ内で 1 回 JavaScript ライブラリを参照し、ポートレッ トの一意の ID を変数およびメソッド名にプリフィックスする必要があります。 場合によっては、3 つ以上のポートレットの接続が必要な場合があります。この ホワイト・ペーパーでは取り上げていませんが、ポートレットが他のポートレッ トに登録し、イベントを「リスニングする」ことが可能なフレームワークを比較 的簡単に作成できます。この方法で、他のポートレットで起きたイベントに基づ いて、ポートレットのコンテンツをリフレッシュすることができます。また、ユーザーがポートレットを iframes 内でレンダリングした場合は、このホワ イト・ペーパーで説明したテクニックを一部変更する必要があることにも注意し てください。Oracle Portal を含めた多くのポータルでは、iframes を使用していま せん。これらの環境では、ここで説明した方法が正しく機能します。 長期的には、これらの問題はポートレット標準の次のバージョン JSR 286 におい て対応する必要があります。
結論
このホワイト・ペーパーでは、ポートレットを使用することによって、今日の Web アプリケーションの可用性を非常に簡単に向上させることができることを説明し ました。また JSR 168 ポートレット API を使用した標準ベースの Ajax ポートレッ トの例を作成する方法を順を追って説明しました。 JSR 168 ポートレットのポートレット間通信は、周知の代替策によって実現できま すが、Ajax を使用するとポートレット間通信を向上させることができます。また、 高機能な対話モデルの説明に加え、実行時にポートレットを接続する方法につい ても説明しました。AJAX を使用した対話性の高いポートレットの構築 著者: Peter Moskovits 2006 年 7 月 Oracle Corporation World Headquarters 500 Oracle Parkway Redwood Shores, CA 94065 U.S.A. 海外からのお問合せ窓口: 電話: +1.650.506.7000 ファックス: +1.650.506.7200 www.oracle.com Copyright © 2006, Oracle. 無断転載を禁ず。 この文書はあくまで参考資料であり、掲載されている情報は予告なしに変更されることがあります。 オラクル社は、本ドキュメントの無謬性を保証しません。また、本ドキュメントは、法律で明示的または暗黙的に記載 されているかどうかに関係なく、商品性または特定の目的に対する適合性に関する暗黙の保証や条件を含む一切の保証 または条件に制約されません。オラクル社は、本書の内容に関していかなる保証もいたしません。また、本書により、 契約上の直接的および間接的義務も発生しません。本書は、事前の書面による承諾を得ることなく、電子的または物理 的に、いかなる形式や方法によっても再生または伝送することはできません。 Oracle はオラクル社またはオラクル社の関連会社(あるいはその両方)の登録商標です。その他の名前は、各社の商標 です。