本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

WebSphere Portal V5.1.0.1 プログラミング・モデルを活用する: 第2部:高度なURL生成

ポータル・ナビゲーション制御の実装

Stefan Behl (stefan.behl@de.ibm.com), Software Engineer, IBM, Software Group
Stefan Behl photo
Stefan Behl is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. He joined the Workplace and Portal Foundation Development in 2003 and works in the Portal Engine team. His main areas of focus are navigational state handling and page aggregation. Stefan studied Software Engineering at the University of Stuttgart, Germany, and holds a diploma in Computer Science.
Stefan Hepper, WebSphere Portal Programming Model Architect, IBM, Software Group
Stefan Hepper is the responsible architect for the WebSphere Portal and Workplace programming model and public APIs. He co-led the Java Portlet Specification V1.0 (JSR 168) and is now leading the V2.0 (JSR 286) effort. Stefan received a Diploma of Computer Science from the University of Karlsruhe, Germany, and in 1998 he joined the IBM Böblingen Development Laboratory.
Stefan Koch (stefkoch@de.ibm.com), Software Engineer, IBM, Software Group
Stefan Koch photo
Stefan Koch is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. As part of the WebSphere Portal Engine Team he is responsible for portal tags and URL generation issues. Stefan studied Information Engineering at the University of Applied Sciences Osnabrueck, Germany.
Carsten Leue (CLEUE@de.ibm.com), WebSphere Portal Runtime Architect, IBM, Software Group
Carsten Leue photo
Dr. Carsten Leue works as an architect within the WebSphere Portal team in the IBM Development Laboratory in Boeblingen, Germany. He has 6 years of experience in the software development field and holds a PhD in physics from the University of Heidelberg, Germany. He joined IBM in 2000 to apply his scientific background on image processing to address recognition for a postal solution. He moved to WebSphere Portal to work on the WSRP OASIS standard as one of the specification editors. After gaining deep insight into WebSphere Portal as a Chief Programmer, Carsten now focuses on the architecture of the Foundation in the areas of state handling and configuration management.

概要: WebSphere Portal V5.1.0.1プログラミング・モデルを活用する:第2部:高度なURL生成 ポータル・ナビゲーション制御の実装

このシリーズの他の記事を見る

日付:  2006年 9月 01日 (公開: 2006年 3月 08日)
レベル:  初級 この記事の原文:  英語
アクティビティー: 3591 ビュー
お気軽にご意見・ご感想をお寄せください: 


これは、WebSphere Portal V5.1.0.1プログラミング・モデルの企業ポータルへの適用を支援する連続記事の第2部です。第1部では、モデルについて解説しました。 第2部では、従来のWebアプリケーション業界のプログラマーにとって難しい領域であるUniformResource Locator(URL)の生成方法を取り上げます。 ポータル環境は、URLの作成が必要なコンポーネントにとって特殊な要件をもたらします。このため、ポータル環境でのURLの生成は少し複雑になります。 この記事では、ポータル・コンポーネントが、ポータルJSPタグよりも優れた機能を持つWebSphere Portalバージョン5.1.0.1 System Programming Interface (SPI)を使用してURLを生成する方法を解説します。 また、ナビゲーション用のパンくずリストを実装した例を用いて、これらのSPIの使用方法についても解説します。

はじめに

モノリシックなWebアプリケーションを作成する開発者は、アプリケーション内のすべてのアスペクトを完全に制御できます。アプリケーション開発者は、必要に応じてURLを自由に設計できます。しかし、ポータル環境では事情が異なります。ポータル・アプリケーション開発者は、他のコンポーネントと結合されて1つの大きなポータル・アプリケーションに組み込まれるコンポーネントを提供します。このため、独自の方法でURLを生成することはできず、開発者はポータル・フレームワークによって提供されるメカニズムに従ってURLを作成する必要があります。

WebSphere Portalでは、マークアップを生成する(このため、URLの作成機能を必要とする)主要コンポーネントは次のとおりです。

  • テーマ:ページ全体のルック・アンド・フィールとページのレイアウトを作成します。
  • スキン:ポートレット・ウィンドウのルック・アンド・フィールを作成します。ウィンドウの状態やポートレットのポートレット・モードを変更するボタンも含まれます。
  • ポートレット:ポートレット・ウィンドウ内のコンテンツをレンダリングします。

この記事では、Java APIを使用したテーマとスキンのURLの生成方法を説明します。テーマとスキンのURLは、ポータルJSPタグ・ライブラリー(参考文献の「WebSphere Portal Information Center, Portal tag library」セクションを参照)のURLタグとURLGenerationタグを使用しても作成できます。これらのタグはほとんどのユース・ケースをカバーするため、一般的なユース・ケースではこれらのタグを使用してください。ただし、Java APIまたは追加機能が必要な状況では、Navigational State SPIと呼ばれるWebSphere Portal V5.1.0.1 System Programming Interface(SPI)を使用することができます。このようなユース・ケースの1つとして、URLを使用して別のテーマのテンプレートに切り替える場合が挙げられます。

他のポートレットを指すURLを生成する必要があるポートレットは、ポートレットAPIを使用しなければなりません(詳細については、参考文献の「JSR 168 Portlet Specification」リンクを参照してください)。たとえば、ポートレットAが別のページにあるポートレットBにパラメーターを送信し、ユーザーをそのページに移動させるユース・ケースを考えます。この場合、ポートレットBを指すURLを作成してページ階層をポートレットAにハード・コーディングする代わりに、ポートレットAでプロパティー・ブローカーのインフラストラクチャーを使用し、イベントを発生させる必要があります。ポータル管理者は、ポートレットAのイベントをポートレットBの入力に結び付けます。この結果、ポータルはそのページへの切り替えを自動的に実行します。詳細については、「参考文献」の『Developing JSR 168 compliant cooperative portlets』を参照してください。

この記事は、ポータルの基本概念(第1部参照)とJSPの基礎を理解し、Javaプログラミングに習熟していることを前提として書かれています。


ステート・ハンドリングの確認

WebSphere Portal V5.1は、ポートレットについてのJSR168ナビゲーション・ステートの概念をポータル・サーバーのステートに拡張します。JSR168モデルは、ポートレットが使用できるステートとして、次のタイプのステートをサポートします。

  • パーシスタント・ステート(Persistent state):PortletPreferences APIを介して公開されます。ポートレットの実装者は、プリファレンスを使用して、ポータルのエンド・ユーザー・セッションでデータを保存および取得できます。
  • セッション・ステート(Session state):PortletSession APIを介して公開されます。セッション・ステートはサーバー上で保持されるデータを表し、このデータはポートレット・コンテナーによって管理される限られたライフタイムを持ちます。セッション・ステートの目的は、一時的な特性を持ち、他のステートや現在の対話からは再計算できない情報(たとえば、ショッピング・カートの中身)を保持することです。テーマとスキンは、HttpSessionを使用してセッション・ステートを使用できます。
  • ナビゲーション・ステート:PortletRequestを使用したレンダリングまたは対話パラメーターの形で公開されます。ナビゲーション・ステートは、ポートレット上の現在のビューに特有な情報(たとえば、選択されているタブ)を表します。このステートのライフタイムは、ポートレット要求のライフタイムと同じです。ポートレットのナビゲーション・ステートは、ポートレット・コンテナーによって、エンド・ユーザーと他のポータル成果物(たとえば、ページ上の他のポートレットまたはテーマ内のナビゲーション)との対話に沿って管理されます。ナビゲーション・ステートの使用は、WebSphere Portalでブラウザーの「戻る」と「進む」ボタンを有効にする際に重要です。ポートレットのレンダリング・パラメーターに加え、WebSphere Portalは、ページ選択、ページ・ナビゲーション・ツリーの展開状態、ポートレットのモードなどのナビゲーション・ステート情報を管理します。すべての指定済みポートレットの結合されたレンダリング・パラメーターは、システムの完全なナビゲーション・ステートを形成します。これは、ポータル・ページの各URLにエンコードしなければなりません。

図1.WebSphere Portalのさまざまなステート

ポートレットを設計するときは、ポートレットが管理するステートを識別し、これらを適切にエンコードする必要があります。特に、ナビゲーション・ステートとセッション・ステートはお互いに直交するよう設計しなければなりません。異なるナビゲーション・ステートと組み合わせて、同じセッションにアクセスできるからです。一般に、これが発生するのは、ユーザーが「戻る」ボタンを使用するとき、ポートレット特有のステートが含まれるブックマークを呼び出すとき、またはオリジナルのウィンドウとセッションを共有する新しいブラウザー・ウィンドウを開き、双方のウィンドウを個別にナビゲートするときなどです。

ポートレットによって生成された各リンクは、次の2つのカテゴリーに分類されます。

  • アクション・リンクは、ポートレットのアクション・フェーズを呼び出すトリガーとなります。ポートレットは、このフェーズでセッション・ステートまたはバックエンド・ステートを自由に変更でき、アクション・フェーズの最後に新しいレンダリング・パラメーターを関連付けます。これらのレンダリング・パラメーターは、アクション要求に続くレンダリング要求で有効になります。アクション・フェーズは、ページ上の他のポートレットがレンダリングを開始する前に呼び出されるため、この柔軟性の短所としてパフォーマンスが低下します。アクションの完了後にのみ、これらのポートレットはレンダリングを開始できます。しかし、WebSphere Portalのパラレル・ポートレット・レンダリング(Parallel Portlet Rendering)機能を有効にすると、これらのレンダリングを並列して実行できます。アクション・リンクは、WWW1のW3Cアーキテクチャー推奨(「参考文献」参照)に適合させるために、常にPOSTリンクとしてエンコードする必要があります。
  • レンダー・リンクは、レンダリング・パラメーターのセットをリンクの呼び出し時に発行される要求と結び付けます。レンダー・リンクの呼び出しの結果として、ポートレットのアクション・フェーズは呼び出されず、ポートレットはレンダリング・パラメーターの新しいセットを使用してそのビューをレンダリングする必要があります。また、レンダー・リンクの呼び出しの結果として、ポートレットはセッション・ステートまたはバックエンド・ステートを変更することはできません。レンダー・リンクは、ポートレットと対話するために最も効率的な方法を提供し(JavaScriptを使用した未加工のクライアント・サイドの対話を除く)、ナビゲーション・ステートが変更された場合にのみ使用します。レンダー・リンクはGETリンクとしてエンコードしなければなりません(「参考文献」参照)。

ポートレットの概念と同様に、テーマとスキンもアクション/レンダー・リンクのセマンティクスを持つリンクを生成します。これは、ポートレット・ステートではなく、ポータル・ステートの変更に関連します。

  • エンジン・アクション(Engine Actions)へのリンクは、ポートレットのアクション・リンクに相当します。このリンクは、サーバー・サイド・ステートを変更できるサーバー・サイド・コマンドを参照するためです。
  • それ以外のすべてのリンク(たとえば、ページ選択またはポートレット・ステート/モードの変更)は、ポートレットのレンダー・リンクに相当します。これらのリンクは、サーバーのステートを変更せずにポータル・サーバーだけの異なるビューを要求し、GETリンクとして表す必要があります(「参考文献」参照)。

ナビゲーション・ステートのエンコード

ナビゲーション・ステートは、特定のクライアントに関連するポータルのビューを表します。クライアントはWebページと対話することにより(たとえば、新しいページへの移動により)、異なるビューを要求(照会)することができます。このタイプの対話はサーバー上の何も変更せず、サーバーによって提供される新しいビューだけを要求します。このため、これはHTTP用語でいう「安全な」オペレーションです。この対話は、ユーザーが最新のビュー間を前後に移動できる特性を持ちます。また、ビューをブックマークしておき、後でブラウザーのブックマークを呼び出すことにより、そのビューに戻ることができます。

開発者は、WebSphere Portalのナビゲーション・ステートをURLにエンコードすることで、この動作を実装します。異なるナビゲーション・ステートからは、異なるURLが生成されます。


Navigational State SPIを調べる

次に、新しいNavigational State SPIの主な概念と、これらの概念がお互いにどのように関連するのかを見ていきましょう。また、アクセサー・アプリケーション・プログラミング・インターフェース(Accessor Application Programming Interface)についても解説します。これは、ナビゲーション・ステート情報へのタイプ付きアクセスを可能にする便利なインターフェースです。

次のセクションで説明するすべてのインターフェースとクラスは、com.ibm.portal.state.* にパッケージングされています。


ナビゲーション・ステートとURLを表す

WebSphere Portal Version 5.1から、ナビゲーション・ステートは、ステート情報の階層として表されます。オブジェクト・モデルはタイプなしのステート情報を含むDOMに似た文書モデルで、java.lang.Stringとして表されます。この設計の決定は、「一般的なポータル・ページは多数のURLを含み、1つの要求につきナビゲーション・ステートが複数回シリアライズされる必要がある」という前提に基づいています。ストリング・ベースのメモリー表現によって、ナビゲーション・ステートのURLへの効率的なシリアライズが可能になります。つまり、シリアライズ・プロセスの際に、時間とCPUを消費する、オブジェクトからストリングへの変換を避けられます。

しかし、プログラマーの観点からは、厳密にタイプ付けされたインターフェースを使用してステート情報にアクセスする方がより便利でフェイルセーフとなります。このため、ステート・ハンドリングAPIには、ナビゲーション・ステート情報へのタイプ付きの読み取りおよび書き込みアクセスを提供する使いやすいインターフェース(アクセサーAPIの一部)が含まれています。「アクセサーAPIを使用したナビゲーション・ステートの読み取りと書き込み」を参照してください。

新しいステート・ハンドリングSPIの設計では、一般的なパターンとして、読み取り専用インターフェースを、読み取りおよび書き込み可能インターフェース(コントローラー)から分離する方法が使用されています。このため、APIはナビゲーション・ステートへの次の2つのインターフェースを提供します。

  • com.ibm.portal.state.StateHolder は、ナビゲーション・ステート情報への読み取りアクセスを提供します。
  • com.ibm.portal.state.StateHolderController はステート情報の変更を可能にします。

図2では、ステート・ホルダーが、タイプなしの文書モデルを内包するラッパーであることが示されています。このような設計となっているのは、まず、現在のナビゲーション・ステートを複製し、その後で、生成されたURLの特定のセマンティクスに従ってナビゲーション・ステートを変更する必要があるためです。たとえば、プログラマーが特定のポータル・ページを指すURLを作成したいとき、ステート文書モデル内のページ選択情報を変更しなければなりません。しかし、ページ上のすべてのURLでこの変更を有効にすべきではありません。


図2.タイプなしのDocumentModelを内包するStateHolder

ステート・ホルダーのライフタイムは1つの要求が処理されるまでの間です。要求を処理するときに、ステート・ホルダーが通過するライフサイクルのフェーズを図3に示します。

  • URLのデコード時に、ポータルはステート・ホルダーを作成し、受信した要求URLから取得したナビゲーション・ステートを割り当てます。次に、URLに含まれるフラットで文字ベースのステート表現を、階層的なオブジェクト表現(前述の文書モデル)に変換します
  • アクションの処理時に、ポータル・エンジンとポートレットはステートを変更できます。アクション・フェーズの処理が完了した後、ポータルはステートを固定し、変更できないようにします。
  • レンダリング時に、指定済みポータル・ページの一部であるポートレットとナビゲーション・コントロールは、URLを生成し、それらをマークアップに含めます。通常、生成されたURLはターゲットのナビゲーション・ステートをエンコードします。このナビゲーション・ステートは、固定されたステート・ホルダー(ベース・ステートとも呼ばれます)と、対応するURLの特定のセマンティクスを表す部分的なステート情報によって構成されます(「URLエンコードを理解する」を参照)。この段階ではベース・ステートを変更できないため、ポータル・エンジンは新しい各URL用に、このベース・ステートの(論理的な)コピーを作成します。このベース・ステートは、URLの目的とするセマンティクスに応じて変更できます。ベース・ステートを維持することにより、前の対話のナビゲーション・ステートは失われません。

図3.StateHolderオブジェクトのライフタイム

URLはcom.ibm.portal.state.EngineURLインターフェースによってモデル化されます。EngineURLはナビゲーション・ステートを含むURLを表します(「URLエンコードを理解する」を参照)。適切なURLファクトリーから新しいEngineURLインスタンスを要求するときに、EngineURLが参照する初期ステート・ホルダーを指定します。通常、これは要求固有のベース・ステートのコピーです。

APIの中心的なオブジェクトであるEngineURLインターフェースの簡単な概要をリスト1に示します。最も重要なメソッドは、この特定のEngineURLインスタンスが参照するステート・ホルダー・オブジェクトを返すgetState()メソッドです。このメソッドは変更可能なインターフェース(StateHolderController)をステート・ホルダーに返すため、プログラマーはこのEngineURL用にステートを変更できます。ステートの変更方法の詳細については、「アクセサーAPIを使用したナビゲーション・ステートの読み取りと書き込み」を参照してください。

writeCopy()メソッドおよびwriteDispose()メソッドによって、EngineURLを指定されたWriter(たとえば、HTTP応答から取得したマークアップPrintWriter)へストリーミングすることができます。(toString()を使用することもできますが、これはかなりコストの高いオペレーションなので、推奨できません。)


リスト1.EngineURLインターフェース
 public interface EngineURL extends DisposableURL {	
    /** Returns a read-write interface to the state carried by this URL */
    StateHolderController getState();
    /** Specifies whether the URL should point to public /protected area */
    void setProtected(Boolean bFlag);	
    /** Specifies whether a secure connection is requested */
    void setSecure(Boolean bFlag);
    /**
     * Streams the URL to the given writer. Maintains the state of the URL
     * i.e. this method can be called multiple times.
     */
    Writer writeCopy(Writer out)
       throws IOException, OutputMediatorException, 
              PostProcessorException, DocumentModelException;
    /**
     * Streams the URL to the given writer and finally releases the state
     * of the URL. The EngineURL object must not be accessed after 
     * invoking this method
     */
    Writer writeDispose(Writer out)
       throws IOException, OutputMediatorException, PostProcessorException;
}


URLエンコードを理解する

WebSphere Portal V5.1に導入されたURLエンコードの実装は、次の点でパフォーマンスが最適化されています。

  • URLの生成に必要な処理時間を最小限にします。特に、URLに含めなければならないナビゲーション・ステートのシリアライズに要する時間が短縮されます。
  • URLをできるだけ短くします。これにより、マークアップのサイズが減少すると共に、一部のブラウザーで行われているURLの長さの制限(2KBまで)に適合します。

これらの要件を満たすために、URLエンコードはデルタ・エンコードと呼ばれる手法を用いて実装されます。デルタ・エンコードは、プログラマーが明示的に他のステートにURLのベースを置かない限り、固定されたベース・ステート(アクション・フェーズの後で利用可能)を、(少なくとも概念的に)各URLに含める必要があるという考えに基づいています。作成された各URLのベース・ステートがシリアライズされるのを防ぐため、アクション・フェーズの直後に前もってシリアライズ(プレシリアライズ)が行われます。URLの生成時に、プレシリアライズされたベース・ステートは、非相対的な各URLに設定されます。URL固有のセマンティクスを指定するステート・デルタは、相対URLのパス情報に個別にエンコードされます。

次の例は、コーデック識別子kcxmlの後にシリアライズされたベース・ステート部分を含む絶対「デルタURL」です。デルタ・コーデック識別子delta/base64xmlの直後に、ステート・デルタ部分があります。

http://myserver.com:9081/wps/portal/kcxml/04_Sj9SPykssy0xPLMnMz0vM0Y_QjHvm5qf
oFuaE6KigCBh9CG/delta/base64xml/Y2BkbGBgYlrDwMDE3srCasTUq!

相対URLを生成できると、状態はさらに改善されます。ブラウザーは、相対URLを現在の要求URLまたはHTMLベース・タグの値(存在する場合)に追加します。WebSphere Portalは、プレシリアライズされたベース・ステートをHTMLベース・タグを使用してHTMLページのヘッダーに挿入することにより、相対URLを活用します。具体的な相対URLには、ステート・デルタのみが含まれます。次の例は、この規則によってマークアップ・サイズが大幅に削減されることを示します。

<head>
    <base href="http://myportal.com:9081/wps/portal/!ut/p/kcxml/04_Sj9SPykssy0xPAIuz!
    </base>
</head>
<body>
...
    <td class="wpsToolBar" valign="middle" nowrap>
       <a href="delta/base64xml/L2dJQSEvUUt3QS80SVVFLzZfQkEwN1VTOVMyMzAwR0!"
          class="wpsToolBarLink">Administration</a>
    </td>
...

相対URLを使用できないケースもあります。たとえば、プロトコルがhttpからhttpsに切り替わる場合は、生成されたURLは絶対URLでなければなりません。また、マークアップでJavaScriptが多用されていると、ブラウザーは相対URLを解決できません。ポータル・ページに含まれるカスタム・マークアップでのこのようなJavaScriptの問題を防ぐために、WebSphere Portal ではデフォルトで相対URLが無効になっています(たとえば、ポートレットによって生成されたマークアップなど)。しかし、Navigational State SPIには、明示的に相対URLを作成する方法が用意されています(詳細については、「URLAccessorFactoryを使用したURLの作成」を参照してください)。


アクセサーAPIを使用したナビゲーション・ステートの読み取りと書き込み

アクセサーAPIは、ステート文書モデルへのタイプ付きアクセスを提供します。アクセサーAPIにより、プログラマーは容易にナビゲーション・ステート情報を照会および変更することができます。

図4に、アクセサーAPIを示します。アクセサーAPIは、特定のノードへのアクセスを階層的な文書モデルにカプセル化する抽象化層です。ナビゲーション・ステートの各様相について(たとえば、ページ選択、展開ステート、ポートレット・ステートなど)、アクセサーAPIはアクセサー・ファクトリーを提供します。アクセサー・ファクトリーは、参照する特定のステートに応じた読み取り専用アクセサーと読み取りおよび書き込みアクセサーを提供します。アクセサーは、ステート文書モデルの対応する位置から、直接読み取りや書き込みを行います。また、アクセサーは必要な型変換も行います。

必要なナビゲーション・ステート情報はステート文書モデル内にあり、専用のアクセサー・ファクトリー・インプリメンテーション内にカプセル化されています。一般に、アクセサー・ファクトリーはパス表記を使用して、ステート文書モデル内で特定の文書ノードを検索したり、ノード(または、ノードの完全なパス)を作成します。ノードが見つかると、アクセサー・ファクトリーは、ノード参照をアクセサーまたはアクセサー・コントロールに、その初期化中に渡します。アクセサー(およびアクセサー・コントローラー)のインプリメンテーションは、ステート文書モデルの構造から独立しています。つまり、必要な情報がステート文書モデル内の別のノードに移動した場合でも、アクセサーを再使用できます。

図4にいくつかの重要なアクセサーを示します。対応するアクセサー・ファクトリーは省略されています。


図4.アクセサーAPI

それでは、選択アクセサー・ファクトリーcom.ibm.portal.state.selection.SelectionAccessorFactoryと、それに関連するすべてのインターフェースを詳しく見ていきましょう。これらをリスト2に示します。


リスト2.SelectionAccessorFactoryインターフェースと関連インターフェース
		        
public interface SelectionAccessorFactory extends AccessorFactory {
    /** Returns a read-only accessor operating on the given state */
    SelectionAccessor getSelection(StateHolder state);
    /** Returns a read-write accessor operating on the given state */
    SelectionAccessorController getSelectionController(StateHolderController state);
}

public interface SelectionAccessor extends Accessor {
    /** Returns the object id of the currently selected portal page */
    ObjectID getSelection() throws InvalidSelectionNodeIdException;
}
public interface SelectionAccessorController extends SelectionAccessor {
    /** Selects the page that corresponds with the given id */
    void setSelection(ObjectID pageID) throws CannotInsertSelectionNodeException;
    /** Selects the page that corresponds with the given unique name */
    void setSelection(String uniqueName) throws CannotInsertSelectionNodeException;
} 

他のアクセサー・ファクトリーと同様に、SelectionAccessorFactoryは読み取り専用のSelectionAccessorを返すメソッドと、読み取りおよび書き込み可能なSelectionAccessorControllerを返すメソッドを公開します。getSelectionAccessor()メソッドは、読み取り専用のStateHolderインターフェースを引数として受け取ります。getSelectionController()には、読み取りおよび書き込み可能なStateHolderControllerインターフェースが必要です。

アクセサーが動作するステートは、必ずしも、要求URLから取得された要求固有のベース・ステートではないため、この軽量のパターン(ステートが引数として渡される)が選択されています。通常、これは特定のEngineURL用に作成されたステート・クローンです。EngineURLオブジェクトにgetState()を呼び出すことにより、URL固有のステート・ホルダーを取得できます。SelectionAccessorControllerを使用して、作成されたEngineURLが特定のポータル・ページ(たとえば、[Stock Market]ページ)を指すようにする方法をリスト3に示します。


リスト3.SelectionAccessorControllerを使用したページ・リンクの作成
 final EngineURL url = ...;
final SelectionAccessorFactory selFct = ...;

final SelectionAccessorController selCtrl = 
    selFct.getSelectionController(url.getState());

try {
    selCtrl.setSelection("wps.StockMarket");
} catch (StateException e) {
    // error handling
    ...
} finally {
    selCtrl.dispose();
}

ベース・アクセサー・インターフェースは、com.ibm.portal.Disposableインターフェースから派生しています。アクセサーが不要になったとき、プログラマーはdispose()メソッドをそのアクセサーに呼び出すことにより、不要なアクセサーを明示的に示すことが推奨されています。これにより、アクセサー・ファクトリー・インプリメンテーションはオブジェクト・プールにアクセサー・ファクトリーを保存し、パフォーマンスを改善することができます(初期化のオーバーヘッドとガーベッジ・コレクションが削減されます)。


PortalStateManager Service を使用したURLの生成

WebSphere Portalバージョン5.1.0.1の新しいサービスであるPortalStateManagerServiceを使用すると、プログラマーはポータルの高度なユース・ケースを実装するURLを容易に作成できます。

このサービスは、次の目的で使用できます。

  • WebSphere Portalタグ・ライブラリー(特に、URL-タグとURLGeneration-タグ)が十分でない場合に、テーマ(JSP)内にURLを作成します。
  • ポータル関連の成果物(カスタム・タグ、カスタム・ポータル・アクションなど)でプログラマチックにURLを作成します。
  • プログラマチックにURLを「オフライン」で作成します。これは、現在のサーブレット要求(たとえば、Enterprise JavaBeans)にアクセスしないビジネス・コンポーネントでの作成を意味します。

次のサブセクションでは、PortalStateManagerServiceへのアクセス方法とURLの作成方法を説明します。最後のサブセクションでは、この新しいサービス(SPIの一部)を使用するカスタムのパンくずリスト・ナビゲーション・タグの実装方法を説明します。

PortalStateManagerServiceへのアクセス

PortalStateManagerServiceにアクセスするには、名前検索ポータルservice/state/PortalStateManagerでJNDI検索を使用します。検索は、特定のcom.ibm.portal.state.service.PortalStateManagerServiceを取得するgetterメソッドを持つcom.ibm.portal.state.service.PortalStateManagerServiceHomeインターフェースを返します。

PortalStateManagerServiceHomeインスタンスは、ポータルのライフタイムで有効です。このため、JNDI検索は1回だけ実行し、取得したホーム・オブジェクトを保存します(たとえば、インスタンス内または静的変数内に保存します)。

PortalStateManagerHomeオブジェクトとは異なり、PortalStateManagerServiceには要求のスコープがあります。つまり、PortalStateManagerServiceは1つのサーブレット要求でのみ使用できます。それ以降のすべてのサーブレット要求では、ホーム・インターフェースからの新しいサービス・インスタンスを要求する必要があります。このサービスへのアクセス方法をリスト4に示します。


リスト4.PortalStateManagerServiceへのアクセス方法
/** the JNDI name to retrieve the PortalStateManagerServiceHome object */
private static final String JNDI_NAME = 
                               "portal:service/state/PortalStateManager";

/**
 * The PortalStateManagerServiceHome object to retrieve the service from 
 */
private static PortalStateManagerServiceHome serviceHome;

/** Any method processing request and response. */
public void anyMethod(final HttpServletRequest request,
                      final HttpServletResponse response) {
    try {
        // get the service from our home interface
        final PortalStateManagerService service
            = getServiceHome().getPortalStateManagerService(request, response);
       // use the service
       ...
       // indicate that we do not need it any longer
       service.dispose();
    } catch (Exception e) {
        // error handling
    }
}
/**
 * Looks up the PortalStateManagerServiceHome being valid
 * for the lifetime of the portal.
 */
private static PortalStateManagerServiceHome getServiceHome() {
    if (serviceHome == null) {
        try {
            final Context ctx = new InitialContext();
            serviceHome =
                (PortalStateManagerServiceHome) ctx.lookup(JNDI_NAME);
        } catch (Exception e) {
            // error handling
        }
    }
    return serviceHome;
} 

PortalStateManagerServiceインターフェースは、com.ibm.portal.Disposableインターフェースから派生しています。サービスが不要になったとき、プログラマーはdisposeメソッドを呼び出すことにより、不要なサービスを明示的に示すことが推奨されています。このサービスは要求スコープを持つため、遅くとも要求の最後ではサービスを破棄する必要があります。

サービスの要求スコープの規則に関し、1つだけ例外があります。要求の処理(または、ポータル)から完全に切り離されている環境またはコンポーネントでこのサービスが使用されている場合、サービスのライフタイムは未定です。このような場合、ホーム・インターフェースからサービスを要求し、要求と応答でnullを渡すことにより、適切なときにそれを破棄できます。

PortalStateManagerServiceの使用

com.ibm.portal.state.PortalStateManagerServiceインターフェースには、次の2つのメソッドがあります。

StateHolderController newState()
変更可能な新しいステート・ホルダーを作成します。いくつかのEngineURL用のベースとして機能する新しいナビゲーション・ステートをプログラマチックに作成するときに、このメソッドを使用します。

AccessorFactory getAccessorFactory(final Class cls)
このサービスによって提供される機能にアクセスできるようにします。決定的な役割は、いわゆるURLAccessorFactoryによって行われます。これにより、プログラマーは、さまざまなユース・ケースをサポートするURLを作成できます。その他すべての取得可能なアクセサー・ファクトリーは、ナビゲーション・ステートの対応するアスペクトを変更することにより、URLの特定のセマンティクスを表現するために必要です。

このサービスを使用するには:

  1. URLAccessorFactoryを使用して新しいEngineURLオブジェクトを取得します。
  2. EngineURLオブジェクトに関連付けられているStateHolderControllerを変更し、作成されたURLのセマンティクスを指定します。getAccessorFactory(Class)メソッドによって取得する適切なアクセサー・ファクトリーを使用します。
  3. マークアップに含めるために、EngineURLを出力ストリーム(たとえば、JSPWriter)に書き込みます。

次に、URLAccessorFactoryを使用してURLを作成する方法を見ていきましょう。


URLAccessorFactoryを使用したURLの作成

com.ibm.portal.state.accessors.url.URLAccessorFactoryには、EngineURLインスタンスを得るために使用できるいくつかの便利なメソッドがあります。

EngineURL newURL(HttpServletRequest request,
	HttpServletResponse response,
	Constants.Clone type);

このメソッドは一般的なすべてのユース・ケースに適しているため、通常、プログラマーはこのメソッドを使用してEngineURLを作成します。このメソッドは、現在のサーブレット要求のナビゲーション・ステートを表すStateHolderを参照するEngineURLを提供します。type引数は、作成するURLのために要求固有のStateHolderをどのように複製するのかを指定します。複製用の定数として、次の4つがあらかじめ定義されています。

  • SMART_COPYは、StateHolderの簡単なコピーを作成することを示します。これは、複製を作成するために文書モデル内のすべてのノードをコピーせず、この特定のEngineURLだけに適用されるステートの変更を記録します。SMART_COPYはデフォルトです。このため、nullを渡すことはSMART_COPYと等価です。この複製メソッドによって、相対「デルタ」URLの生成も可能になります。
  • DEEP_COPYは要求固有のStateHolderの完全なコピーを作成します。各ノードとノード階層が複製されます。DEEP_COPYによって、デルタURLは生成されません。
  • EMPTY_COPYは、要求固有のStateHolderの内容を消去することを示します。言い換えると、作成されたEngineURLは空のステートをベースとします。このようなURLによる対話では、前の対話のナビゲーション・ステートが失われます。
EngineURL newURL(HttpServletRequest request,
	HttpServletResponse response,
	StateHolder state,
	Constants.Clone type);

2番目のnewURLメソッドでは、EngineURLがベースとするStateHolderを明示的に渡す必要があります。type引数はこの特定のStateHolderを参照します。作成されるURLが、プログラマチックに定義されたStateHolderをエンコードしなければならないときに、このメソッドを使用します。一般に、このようなURLをクリックすると、ポータルとの前の対話のナビゲーション・ステートが失われます。

       
EngineURL newURL(HttpServletRequest request,
	HttpServletResponse response,
	URLContext ctx,
	Constants.Clone type);

このメソッドでは、作成されるURLが、絶対URL、サーバーの相対URL、または相対URLのいずれであるかを指定します。URLContextインターフェースを渡し、実装します。たとえば、URLが絶対URLでなければならない場合は、isAbsolute()メソッドはtrueを返し、isRelative()とisServerRelative()はfalseを返す必要があります。このメソッドは明示的なStateHolder引数を必要としないため、EngineURLは指定された要求から取得したStateHolderをエンコードします。マークアップのサイズを減らすには、相対URLを許可するURLContextを渡します。相対URLの詳細については「URLエンコードを理解する」を参照してください。

EngineURL newURL(HttpServletRequest request,
	HttpServletResponse response,
	StateHolder state,
	URLContext ctx,
	Constants.Clone type)

このメソッドは、前のメソッドに対応するもので、プログラマチックに定義された StateHolderを明示的な引数として受け取ります。

EngineURL newURL(ServerContext request,
	boolean isSecure,
	boolean isProtected,
	StateHolder state,
	Constants.Clone type) 

この最後のnewURLメソッドは、サーブレット要求も応答も必要としません。このメソッドは、URLを「オフライン」で生成するときに使用します。つまり、EJBまたは現在のサーブレット要求にアクセスしないJava成果物でのURLです。ホスト名、サーバー・ポート、コンテキスト・パス、およびサーブレット・パスを明示的に渡します。この種類の情報をカプセル化する抽象化として、ServerContextインターフェースが用いられます。ブール値のisSecure引数は、URLがセキュアな接続となるか(https)、ならないか(http)を指定します。2番目のブール値の引数isProtectedは、URLがポータルの保護された領域を指すかどうかを宣言します。これらのパラメーターのいずれかでnullが渡されると、現在の要求のプロパティーが保持されます。

上記のいずれかのメソッドを使用してEngineURLオブジェクトを取得した後は、「アクセサーAPIを使用したナビゲーション・ステートの読み取りと書き込み」での説明に従って、URLのナビゲーション・ステートを変更できます。さまざまなアクセサー・ファクトリーにアクセスするために、java.lang.Object型のオブジェクトを返すgetAccessorFactory(Class)サービス・メソッドを使用します。渡されたClassオブジェクトに従って、返されたオブジェクトを特定のアクセサー・ファクトリー・インターフェースにキャストできます。

これらのメソッドを使用する例をリスト5に掲載します。createPortletLinkメソッドは、指定されたポートレットを指すURLを作成し、そのナビゲーション・ステート(ポートレット・モード、ウィンドウ・ステート、およびレンダリング・パラメーター)を変更する方法を示します。2番目のサンプルのcreateOfflinePageLinkは、現在の要求および応答にアクセスせずに、特定のテーマを持つポータル・ページへのURLを作成する方法を示します。

サンプルのどのメソッドも、適切なnewURLメソッドを使用してURLAccessorFactoryからEngineURLを取得しています。また、URLのナビゲーション・ステートを変更し、指定されたWriterへURLをストリーミングします。


リスト5.ポートレットのナビゲーション・ステートを変更するURLの作成

public Writer createPortletLink(
     final HttpServletRequest req, final HttpServletResponse res,
     final ObjectID portletWindowID, final PortletMode newMode,
     final WindowState newState, final Map renderParameters,
     final Writer writer) throws StateException, IOException {
     
     final PortalStateManagerService service
         = getServiceHome().getPortalStateManagerService(req, res);
     
     // get a URL from the URL accessor factory using request and response
     final URLAccessorFactory urlFct = (URLAccessorFactory) 
         service.getAccessorFactory(URLAccessorFactory.class);
     // the URL should be based on the current request state
     final EngineURL url = urlFct.newURL(request, response, null);
     
     // change the portlet's navigational state
     final PortletAccessorFactory portletFct = (PortletAccessorFactory)  
         service.getAccessorFactory(PortletAccessorFactory.class);
     final PortletAccessorController portletCtrl
         = portletFct.getPortletController(portletWindowID, url.getState());
     try {
         portletCtrl.setPortletMode(newMode);
         portletCtrl.setWindowState(newState);
         portletCtrl.getParameters().putAll(renderParameters);
     } finally {
         portletCtrl.dispose();
     }
     // stream the URL using the given writer
     return url.writeDispose(writer);
} 
 


リスト6.ローカライズされたオフラインのポータル・ページ・リンクの作成
    		    
public Writer createOfflinePageLink(
    final ServerContext serverCtx, final boolean isSecure,
    final boolean isProtected, final ObjectID pageID,
    final Locale locale, final Writer writer)
    throws StateException, IOException {
    
    // get the service via the home interface
    final PortalStateManagerService service
        = getServiceHome().getPortalStateManagerService(null, null);
    
    // get a URL from the URL accessor factory
    final URLAccessorFactory urlFct = (URLAccessorFactory)
        service.getAccessorFactory(URLAccessorFactory.class);
    final EngineURL url = urlFct.newURL(serverCtx, isSecure, isProtected,
        service.newState(), null);
    
    // change the page selection and the locale
    final SelectionAccessorFactory selFct = (SelectionAccessorFactory) 
        service.getAccessorFactory(SelectionAccessorFactory.class);
    final ThemeTemplateAccessorFactory themeTemplateFct = 
        (ThemeTemplateAccessorFactory) service.getAccessorFactory
        (ThemeTemplateAccessorFactory.class); 
    final SelectionAccessorController selCtrl
        = selFct.getSelectionController(url.getState());
    final ThemeTemplateAccessorController themeTemplateCtrl = 
        themeTemplateFct.getThemeTemplateAccessorController(url.getState()); 
    
    
    try {
        selCtrl.setSelection(pageID);
        themeTemplateCtrl.setThemeTemplate(themeTemplate); 
    } finally {
        // dispose the controllers
        selCtrl.dispose();
        themeTemplateCtrl.dispose();
    }

より高度なユース・ケースをサポートするために、PortalStateManagerServiceは追加のアクセサー・ファクトリーを提供します。利用可能なアクセサー・ファクトリーを以下のリストに示します。どのアクセサー・ファクトリーも、パッケージcom.ibm.portal.state.accessors.*の一部です。

SelectionAccessorFactory
SelectionAccessorFactoryは、ポータル・ページ選択情報の読み取りおよび書き込みを行うアクセサーを提供します。他のページを指すURLを作成するときは、作成済みのEngineURLが関連付けられているステートで新しい選択を設定するために、このファクトリーからSelectionAccessorControllerを要求する必要があります。
PortletAccessorFactory
PortletAccessorFactoryは、ポートレットに関連するナビゲーション・ステート情報の読み取りおよび書き込みを行うアクセサーを提供します。この情報には、ポートレット・モード、ウィンドウ・ステート、およびレンダリング・パラメーターが含まれます。特に、PortletAccessorControllerは、指定されたポートレットのナビゲーション・ステート(たとえば、ポートレット・モード)の変更に使用されることもあります。
PortletTargetAccessorFactory
PortletTargetAccessorFactoryは、ポートレット・アクションに関連する情報の読み取りおよび書き込みを行うアクセサーを提供します。特に、PortletAccessorControllerは、ポートレットをアクションのターゲットとして宣言するために使用されることがあります。PortletAccessorControllerによって、プログラマーはポートレット・アクションを起動するURLを作成できます。
SoloAccessorFactory
SoloAccessorFactoryは、ポートレットのSoloビューを参照するアクセサーを提供します。SoloAccessorControllerは、特定のポートレットのSoloビューを起動または停止するURLの作成に使用できます。
ThemeTemplateAccessorFactory
ThemeTemplateAccessorFactoryは、テーマ・テンプレート情報の読み取りおよび書き込みをサポートします。特に、ThemeTemplateAccessorControllerは、特定のテーマ・テンプレートに切り替えるURLの作成に使用されることがあります。
ExpansionStatesAccessorFactory
ExpansionStatesAccessorFactoryは、展開ステート情報の読み取りおよび書き込みを行うアクセサーを提供します。展開ステート情報とは、ナビゲーション・ツリー・コントロールで、指定されたナビゲーション・ノードが展開されているか、折りたたまれているかを示す情報です。一般に、ExpansionStatesAccessorControllerは特定のナビゲーション・ノードの展開ステートを切り替えるツイスティーURLの生成に使用されます。
ShowToolsAccessorFactory
ShowToolsAccessorFactoryは、「表示ツール(show tools)」に関連する情報の読み取りおよび書き込みを行うアクセサーを提供します。一般に、ShowToolsAccessorControllerは、対応するポートレットの移動/削除機能を提供する、ポートレット・ウィンドウのツール・アイコンに組み込まれるURLの作成に使用されます。
StatePartitionAccessorFactory
StatePartitionAccessorFactoryは、ステート・パーティション識別子の読み取りおよび書き込みを行うアクセサーを提供します。StatePartitionAccessorControllerは、ステート・パーティション識別子をナビゲーション・ステートに含めるために使用できます。新しいステート・パーティション識別子は、新しいブラウザー・ウィンドウを開くURLに含める必要があります。
EngineActionAccessorFactoryEngineActionAccessorFactoryは、エンジン・アクションURLの作成に使用しなければならないコントローラーを提供します。特に、EngineActionAccessorControllerによって、アクション・パラメーターを設定できます。エンジン・アクションの実行はポータルによって排他的に管理されるため、このアクセサー・ファクトリーは読み取り専用のアクセサーを提供しません。


例:テーマ用のパンくずリスト・ナビゲーション・コントロール

APIの使い方を説明するために、カスタムJSPタグでAPIをどのように使用するのかを見ていきましょう。

テーマまたはスキンで使用できるパンくずリスト・ナビゲーション・コントロールを含む例を図5に示します。パンくずリスト・ナビゲーションは現在選択されているページ上にあり、コンテンツ・ルートから選択されたページに至るまでのナビゲーション・パスに含まれるすべてのページ・タイトルを表示します。タイトルはページ・リンクとしてレンダリングされるので、ユーザーは現在のページよりも上位のページに直接移動することができます。図5では、ユーザーは[Company Tracker]ページを選択しています。


図5.テーマでのパンくずリスト・ナビゲーションの使用

パンくずリスト・ナビゲーションをテーマのJSPに含める方法をリスト7に示します。この例では、WebSphere Portalのデフォルト・テーマ(Default.jsp)が選択されています。


リスト7.パンくずリストをJSPに含める

<td colspan="2" class="breadcrumb" >
            <crumbtrail:loop var="selection">
            &nbsp;>&nbsp;;
            <a href='<crumbtrail:url node="<%= selection %>" 
                allowRelative="true"/>'>
                <wps:title varname="<%=selection%>"/>
            </a> 
        </crumbtrail:loop><br>
    </td>

このリストでは、2つのカスタムJSPタグを使用してパンくずリストを生成しています。どちらのタグも、crumbtrail接頭辞によって含められたタグ・ライブラリーの一部です。

  • LoopTag(crumbtrail:loop)は、ナビゲーション・パスの検索をカプセル化します。
    内部では、識別されたナビゲーション・パスをたどり、訪れた各ナビゲーション・ノードをselectionと呼ばれるスクリプト変数に格納します。LoopTagは、マークアップ出力をまったく生成しません。
  • UrlTag(crumbtrail:url)は、現在のナビゲーション・ノード(つまり、ページ)を指すURLを作成する役割を持ちます。これは、タグ属性nodeを使用して、対応するナビゲーション・ノードを取得します。この属性に、スクリプト変数selectionの値を単純に代入できます(node="<%= selection %>")。
    内部では、PortalStateManagerServiceを使用してページURLが作成され、適切なJSPWriterを使用してURLがマークアップに書き込まれます。このため、URLをHTMLのhref属性として直接マークアップに含めることができます。作成済みリンクのローカライズされた表示名は、ポータルのtitleタグを使用して作成されます。

次のセクションでは、UrlTagについて詳しく見ていきます。LoopTagは新しいSPIを使用しないため、詳しくは説明しません。LoopTagはModelSPIを使用してナビゲーション・モデルを横断し、パンくずリストを作成します。詳細については、「参考文献」の「WebSphere Portal V5.1.0.1 Information Center, Model SPI Overview section」参考文献を参照してください。

URLタグ内でのPortalStateManagerServiceへのアクセス

PortalStateManagerServiceへのアクセス」で説明したように、JNDIを使用して取得したPortalStateManagerHomeオブジェクトからPortalStateManagerServiceを入手できます。

UrlTagというカスタム・タグを使用してサービスの取得を実装する方法をリスト8に示します。


リスト8.タグ内でのPortalStateManagerServiceへのアクセス

public class UrlTag implements Tag
    
    /** the JNDI name to retrieve the PortalStateManagerServiceHome object */
    private static final String JNDI_NAME = 
        "portal:service/state/PortalStateManager";
    
    /** the PortalStateManagerServiceHome object to retrieve the service from */
    private static PortalStateManagerServiceHome serviceHome;
    
    /** the PortalStateManagerService for the current request */
    private PortalStateManagerService service;
    
    /**
    * Initializes the tag on a per page basis. Gets the request-specific
    * PortalStateManagerService from the service home.
    */
    public void setPageContext(final PageContext pc) {
        pageContext = pc;
        try {
            service = getServiceHome().getPortalStateManagerService(
                (HttpServletRequest)pageContext.getRequest(),
                (HttpServletResponse)pageContext.getResponse());
        } catch (Exception e) {
            logger.log(Level.WARNING,"Exception occured", e);
        }
    }
/**
    * Looks up the PortalStateManagerServiceHome being valid
    * for the lifetime of the portal.
    */
    private static PortalStateManagerServiceHome getServiceHome() {
        if (serviceHome == null) {
            try {
                final Context ctx = new InitialContext();
                serviceHome =
                    (PortalStateManagerServiceHome) ctx.lookup(JNDI_NAME);
            } catch (Exception e) {
                logger.log(Level.WARNING,"Exception occured",e);
            }
        }
        return serviceHome;
    }
    
    /**
    * @see javax.servlet.jsp.tagext.Tag#release()
    */
    public void release() {
            service.dispose();
            service = null;
    } 

PortalStateManagerServiceHomeオブジェクトは、ポータルのライフタイムで有効です。このため、このオブジェクトはJNDIを使用して1度だけ取得され、静的変数serviceHomeに格納されます。検索ロジックは、プライベート・メソッドgetServiceHome()に実装されています。PortalStateManagerServiceはsetPageContext()メソッド内で必要です。このメソッドは、ページを訪れるたびに1回呼び出されます。このため、ページごとにURLTagが複数回呼び出される場合に(このサンプルで示すケースです)、同じサービス・インスタンスを再利用できます。ページ・スコープは必要に応じて暗黙的に要求スコープを示すので、これは正しいソリューションです。

release()メソッド内で、タグはサービス自身のdispose()メソッドを呼び出すことにより、これ以上サービスが必要でないことを明示的に示します。URLTagのすべての呼び出しが処理され、タグのライフ・サイクルの終了が示された後に、release()メソッドがJSPインプリメンテーションによって呼び出されます。

ナビゲーション・リンクの作成

パンくずリスト・ナビゲーション内のページ・リンクを表す各URLは、URLTagタグのdoStartTag()メソッド内で作成されます。これに必要なコードをリスト9に示します。


リスト9.doStartTag内でのナビゲーション・リンクの作成
    
    /** The navigation node to operate on */
    private NavigationNode iNode;
    
    /** Flag indicating whether this tag may create a relative URL */
    private Boolean iAllowRelativeURL;
    
    /** URL context that allows for relative URLs */
    private static final URLContext ALLOW_RELATIVE_URL = new RelativeURLContext();
    
    /** URL context that disallows relative URLs */
    private static final URLContext DISALLOW_RELATIVE_URL = new NonRelativeURLContext();
    
    /** Sets the navigation node to operate on. */
    public void setNode(final NavigationNode node) {
    iNode = node;
    }
    
    /** Specifies whether the URL to be created may be relative or not. */
    public void setAllowRelativeURL(final String allowRelative) {
    iAllowRelativeURL = Boolean.valueOf(allowRelative);
    }
    
    /**
    * Create an EngineURL which points to the portal page represented by iNode.
    * Finally write URL to the responsible JSPWriter and indicate that no tag
    * body processing is required.
    */
    public int doStartTag() throws JspException {
        if (iNode != null) {
            try {
                // get the factory providing EngineURLs
                final URLAccessorFactory urlAccessorFactory = 
                    (URLAccessorFactory) service.getAccessorFactory 
                        (URLAccessorFactory.class);            
                // request an EngineURL 
                final EngineURL selectionURL;
                // request and response
                final HttpServletRequest request =
                    (HttpServletRequest) pageContext.getRequest();   
                final HttpServletResponse response =
                    (HttpServletResponse) pageContext.getResponse();   
                // check whether we can create a relative URL
                if (iAllowRelativeURL == null) {
                    // the server should decide
                    selectionURL = urlAccessorFactory.newURL(
                        request, response, null);
                } else if (iAllowRelativeURL.booleanValue()) {
                    // relative URLs are allowed
                    selectionURL = urlAccessorFactory.newURL(
                        request, response, ALLOW_RELATIVE_URL, null);
                } else {
                        // relative URLs are not allowed
                                selectionURL = urlAccessorFactory.newURL(
                            request, response, DISALLOW_RELATIVE_URL, null);
                }
                // get the selection accessor factory    
                final SelectionAccessorFactory selectionAccessorFactory = 
                    (SelectionAccessorFactory) service.getAccessorFactory
                            (SelectionAccessorFactory.class);           
                // get the selection controller which operates on the URL-
                // specific state which has been derived from the request state 
                final SelectionAccessorController selectionAccessorController = 
                    selectionAccessorFactory.getSelectionController
                            (selectionURL.getState());
                // set the ObjectID of iNode
                selectionAccessorController.setSelection(iNode.getObjectID());
                // we do not need the controller any more
                selectionAccessorController.dispose();				
                // stream the URL to the obtained JSPWriter and finally dispose
                // the EngineURL object
                selectionURL.writeDispose(pageContext.getOut());
            } catch (Exception e) {
                logger.log(Level.WARNING,"Exception occured", e);
            }
    }
    // no body processing required	
    return SKIP_BODY;
    }
    
    /** Reset our instance variables to enable tag pooling. */
    public int doEndTag() throws JspException {
            iNode = null;
            iAllowRelativeURL = null;
            return EVAL_PAGE;
    }
    
    /** URL context implementation that also allows for relative URLs */
    private static class RelativeURLContext implements URLContext, Serializable {
            /** @see com.ibm.portal.state.accessors.url.URLContext#isAbsolute() */
            public boolean isAbsolute() {
                return true;
            }
            /** @see com.ibm.portal.state.accessors.url.URLContext#isRelative() */
            public boolean isRelative() {
                    return true;
            }
            /** @see com.ibm.portal.state.accessors.url.URLContext#isServerRelative() */
            public boolean isServerRelative() {
                    return true;
            }
    }
    
    /** URL context implementation that enforces non-relative URLs */
    private static class NonRelativeURLContext extends RelativeURLContext {
            /** @see com.ibm.portal.state.accessors.url.URLContext#isRelative() */
            public boolean isRelative() {
                return false;
            }
    } 
    

URLTagは2つのインスタンス変数iAllowRelativeとiNodeを持ちます。Boolean型のインスタンス変数iAllowRelativeは、タグが相対URLを作成できるかどうかを示します。対応する(オプションの)タグ属性はallowRelativeです。デフォルトでは、iAllowRelativeは設定されません(nullと等価です)。これは、サーバーが相対URLを生成できるかどうかを決定する必要があることを示します。URLを相対URLにできるかどうかの判断は、URLをどのようにマークアップに含めるのかに大きく依存します。この例では、作成されるナビゲーションURLはアンカー・タグのhref属性として含められるため、相対URLを使用できます(図9参照)。パンくずリスト・コントロールのマークアップ・サイズを小さくするために、できるだけ相対URLを使用するようにしてください。

iAllowRelative変数を評価するコードは、doStartTagメソッドの一部です。EngineURLは、URLAccessorFactoryから次のいずれかのメソッドを使用して要求されます。ポータル・サーバーが決定する場合は、newURL(HttpServletRequest,HttpServletResponse,Constants.Clone)メソッドが使用されます。相対URLの生成が明示的に許可または禁止される場合は、newURL(HttpServletRequest,HttpServletResponse,URLContext,Constants.Clone)メソッドが使用されます。後者の場合は、newURLメソッドに渡されるURLContext引数は、RelativeURLContext内部クラスのインスタンス(相対URLの生成を許可)、またはNonRelativeURLContext(非相対URLを強制)のいずれかとなります。

NavigationNode型のインスタンス変数iNodeは、タグが参照するページを表します。JSPで使用される、対応する(必須)タグ属性はnodeです。作成されるEngineURLにこのページを選択させるために、ナビゲーション・ノードiNodeのObjectIDを渡して、SelectionAccessorControllerのsetSelectionメソッドを呼び出します。

doStartTagメソッドの最後で、selectionURL.writeDispose(pageContext.getOut())を呼び出すことにより、作成されたページ選択URLをJSP Writerに直接ストリームします。


まとめ

ナビゲーション・ステートは、特定のクライアントに関連するポータルのビューを表します。WebSphere Portal V5.1から、ナビゲーション・ステートがURLにエンコードされ、さまざまな重要な機能を使用できるようになりました。ユーザーは、ブラウザーの「戻る」ボタンと「進む」ボタンを使用して、これまでに訪れたビューを前後に移動できます。また、ブラウザーのブックマークをクリックすることで、特定のポータル・ビューにいつでも戻ることができます。

ナビゲーション・ステートのURLへの効率的なエンコードは、Navigational State SPIインプリメンテーションによって内部的に行われます。プログラマーに必要なのは、Accessor APIを使用して、ユース・ケースに応じて、対応するナビゲーション・ステートを作成済みのURLオブジェクトに設定することです。アクセサーAPIは、特定のナビゲーション・ステートのアスペクト(ページ選択、テーマ、ポートレットに関連するステートなど)の読み取りおよび書き込みを行う直感的なタイプ付きインターフェースを提供します。これにより、プログラマーは型変換から解放されます。

PortalStateManagerServiceは、テーマ・プログラマーにNavigational State SPIへのアクセスを提供する使いやすいポータル・サービスです。これを使用することにより、プログラマーは、デフォルトのポータルURLタグによって提供された機能を拡張するカスタム・タグの開発や、オフラインでのURLの作成(たとえば、EJB内において)など、さまざまなユース・ケースを実装できます。

シリーズの次の記事では、既存のセキュリティー環境にWebSphere Portalを統合する方法について解説します。シングル・サインオン(SSO)やログイン/ログアウト動作のカスタマイズなどのトピックを取り上げます。


既知の問題

WebSphere Portalバージョン5.1.0.1およびバージョン5.1.0.2で相対URLを活用するには、APARPK15894に対する修正をインストールする必要があります。



ダウンロード

内容ファイル名サイズダウンロード形式
コードサンプルcrumbtrail.zip12KBFTP|HTTP

ダウンロード形式について


参考文献

著者について

Stefan Behl photo

Stefan Behl is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. He joined the Workplace and Portal Foundation Development in 2003 and works in the Portal Engine team. His main areas of focus are navigational state handling and page aggregation. Stefan studied Software Engineering at the University of Stuttgart, Germany, and holds a diploma in Computer Science.

Stefan Hepper

Stefan Hepper is the responsible architect for the WebSphere Portal and Workplace programming model and public APIs. He co-led the Java Portlet Specification V1.0 (JSR 168) and is now leading the V2.0 (JSR 286) effort. Stefan received a Diploma of Computer Science from the University of Karlsruhe, Germany, and in 1998 he joined the IBM Böblingen Development Laboratory.

Stefan Koch photo

Stefan Koch is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. As part of the WebSphere Portal Engine Team he is responsible for portal tags and URL generation issues. Stefan studied Information Engineering at the University of Applied Sciences Osnabrueck, Germany.

Carsten Leue photo

Dr. Carsten Leue works as an architect within the WebSphere Portal team in the IBM Development Laboratory in Boeblingen, Germany. He has 6 years of experience in the software development field and holds a PhD in physics from the University of Heidelberg, Germany. He joined IBM in 2000 to apply his scientific background on image processing to address recognition for a postal solution. He moved to WebSphere Portal to work on the WSRP OASIS standard as one of the specification editors. After gaining deep insight into WebSphere Portal as a Chief Programmer, Carsten now focuses on the architecture of the Foundation in the areas of state handling and configuration management.

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Lotus, WebSphere
ArticleID=339190
ArticleTitle=WebSphere Portal V5.1.0.1 プログラミング・モデルを活用する: 第2部:高度なURL生成
publish-date=09012006
author1-email=stefan.behl@de.ibm.com
author1-email-cc=
author2-email=sthepper@de.ibm.com
author2-email-cc=
author3-email=stefkoch@de.ibm.com
author3-email-cc=
author4-email=CLEUE@de.ibm.com
author4-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。