本文へジャンプ

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


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

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

  • 閉じる [x]

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

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

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


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

  • 閉じる [x]

Java Portlet Specification V2.0 (JSR 286) の新たな内容とは?

Stefan Hepper, WebSphere Portal Programming Model Architect, IBM
Stefan Hepper は、WebSphere Portal および Workplace のプログラミング・モデルと公開 API を担当するアーキテクトです。Java Portlet Specification V1.0 (JSR 168) のリーダーの 1 人であった彼は、現在 V2.0 (JSR 286) への取り組みでリーダーを務めています。Stefan はドイツの University of Karlsruh でコンピューター・サイエンスの学位を取得し、1998年に IBM Böblingen Development Laboratory に入社しました。
Oliver Köth, Lead Developer, Portlet Container, IBM
Oliver Koeth photo
Oliver Köth は、WebSphere Portal のポートレット・コンテナーの開発リーダーで、この製品での Java Portlet Specification 2.0 実装を担当しています。ドイツの University of Erlangen でコンピューター・サイエンスの学位を取得し、2001年から IBM Böblingen Development Laboratory で IBM のポータル・ミッションに取り組んでいます。

概要: Java Portlet Specification のバージョン 2.0 (JSR 286) の全容を学んでください。仕様の内容と API の数が 2 倍以上になったバージョン 2.0 では、ほとんどの使用事例をベンダーの拡張機能なしで実装することができます。また、ポートレットのプログラミング・モデルにはイベントと共通レンダリング・パラメーターが用意されているため、現在使用しているポートレットから大規模な複合アプリケーションを構築したり、さまざまなシナリオでポートレットを再利用したりすることができます。

日付:  2008年 3月 18日
レベル:  中級 この記事の原文:  英語
アクティビティー: 4748 ビュー
お気軽にご意見・ご感想をお寄せください: 


ポートレットとは、ある種のマークアップを作成する、コンポーネント化された、ユーザー向けアプリケーションアプリケーションのことです。このマークアップは他のマークアップ・フラグメントと集約されて、より大きな 1 つのアプリケーション (例えば、図 1 に示すようなポータル・ページ) になるように意図されています。


図 1. サンプル・ポータル・ページ

このように、ポートレットはサービス指向アーキテクチャー (SOA) の手法をユーザー・インターフェースにまで適用したユーザー・インターフェース (UI) ベースのサービスとして捉えることができます。

Java ポートレットがよく使われるようになったのは、Java Portlet Specification の最初のバージョンである JSR (Java Specification Request) 168 が 2003年にJava Community Process を終了してからのことです。それ以来、Java ポータルの世界では商用ベンダー、オープンソース・ベンダーを問わず、ほとんどすべてのベンダーがこの標準を実装するようになり、開発者たちも Java Portlet API を使ってポートレットを作成するようになっています。

しかし、JSR 168 では全体的な UI コンポーネント・モデルを定義するにとどめられており、これらのコンポーネントを統合して複合アプリケーションを構築する手段については何も定義していませんでした。こうした複合アプリケーションを構築する手段や、時間的制約から Java Portlet Specification V1.0 に盛り込まれなかった他の多くの事項に対処しているのが、今回の V2.0 です。

JSR 286 の取り組みは 2006年1月に開始され、その最終バージョンは 2008年2月に提出されました。JSR 286 のエキスパート・グループを構成するメンバーは、いずれも主要な商用およびオープンソースのポータル開発者、ポートレット・ブリッジ開発者、そしてポートレット・フレームワーク開発者です。エキスパート・グループのメンバーの一覧を見るには、ここをクリックしてください。

この記事では JSR 286 の主な新機能を概説するとともに、これらの機能の使用方法を説明する例をいくつか紹介します。この記事が対象としているのは、Java Portlet Specification のバージョン 1.0 で定義されたポートレット・プログラミング・モデルについて基本的な知識を持っている読者です。この前提に当てはまらない方は、JSR 168 の概要を読んでください。

ポートレット間の連携を利用した複合アプリケーションの構築

Java Portlet Specification のバージョン 2.0 ではその主要な新機能のおかげで、各種ポートレットの連携が可能になっており、さまざまなパーティーにより実装されるポートレットや、異なる WAR ファイルにパッケージ化されたポートレットを連携させることができます。JSR 286 の連携機能がベースとするのは疎結合のパブリッシュ/サブスクライブ・モデルです。そのため、ポートレット開発者同士が互いの作業内容を知る必要はありません。ポートレットに解釈されるデータを開発時に定義してさえおけば、ポートレット間の実際の接続はデプロイメント時または実行時に作成されます。このような連携機能を利用すれば、ポータル管理者やビジネス・ユーザーはプログラミングを行わずに、ポートレット・コンポーネントを組み合わせて規模の大きな複合アプリケーションを構築することができます。こうしたことができるおかげで、人気の Web 2.0 と同じように、あるいはコンポーネントの作成者が考えもしなかったような方法で、既存のコンポーネントを組み合わせてビジネス・マッシュアップとすることにより、新しい機能を作成できる可能性が広がります。

JSR 286 では、2 種類の使用事例に応じた以下の 2 つの連携手段を定義しています。

  • イベント。ポートレットにアクションを起こさせるためのアクティブな通知をサポートします。

  • 共通レンダリング・パラメーター。ポートレット間でビューの状態が共有されるようにサポートします。

以降のセクションで、2 つの使用事例について詳しく説明します。

イベント

JSR 286 では、ポートレットはイベントを送受信することができます。前述したように、モデルは疎結合モデルであるため、ポータル・アプリケーションは異なるポートレット間のブローカーの役割を果たし、イベントを配布します。


図 2. Portlet 1 からPortlet 2 および 3 へのイベント配布

図 2 に、このイベントを配布する仕組みの詳細を示します。Portlet 1 では、Portlet 1 がイベント A (event:A) をこのポートレットの portlet.xml デプロイメント記述子でパブリッシュできるように定義しています。一方、Portlets 2 および 3 では、Portlets 2 および 3 がイベント A を受信できるように、それぞれの portlet.xml 記述子の中で定義しています。そして、ポータル管理者またはビジネス・ユーザーは、この 3 つのポートレットをページに配置し、送信側ポートレットである Portlet 1 と受信側ポートレットである Portlet 2 および Portlet 3 との間に接続を作成しました。ここでユーザーが Portlet 1 を操作して Portlet 1 にアクションを起こさせると、Portlet 1 はイベント A を発行します。Portlet 1 はイベント・ブローカー・コンポーネント (通常はポータル・ランタイムの一部) を処理してそのアクションを終了した後、このイベントのターゲットを検索します。上記の例では、Portlet 1 と Portlet 2 および 3 との間にこのイベントに対する接続があるため、イベント・ブローカーはイベント A をペイロードとして、Java Portlet Specification のバージョン 2.0 で導入された processEvent ライフサイクル・メソッドを Portlet 2 および 3 で呼び出します。このアクションとイベント処理が終了するとレンダリング・ライフサイクルが開始され、この 2 つのポートレットがアクションおよびイベント・フェーズで行われた状態更新に基づいて新しいマークアップを作成できるようになります。

JSR 286 では、ポートレットが連携するための接続を定義および管理する方法を規定していません。一般に、接続はページの作成時に明示的に定義されるか、あるいは実行時にポータルが自動的に接続を推定します。前者はより柔軟な制御が可能である一方、後者は使いやすいという違いはありますが、いずれの場合にしても、ポータルはポートレットが受信および送信できるイベントの種類を認識する必要があります。そのためには、portlet.xml デプロイメント記述子にこの情報を含めなければなりません。それにはどうすればよいかと言うと、まず一般的なイベント定義をした上で、ポートレットがこのイベントを受信または送信できることを指定するタグを設定したポートレット・セクションでこの定義を参照します。それぞれのイベントを一意に識別するのは、XML 修飾名 (QName) です。QName は XML で一意に決まる名前を使用するためのメカニズムで、Java による表現も標準ライブラリー・クラス javax.xml.namespace.QName として持ち合わせています。QName は、名前空間 (http://www.ibm.com など) とローカル部分 (myEvent など) で構成されます。

リスト 1 にその一例を示します。


リスト 1. パブリッシング・イベント定義の例
                
	<portlet>
		…
		<supported-publishing-event>
			<qname xmlns:x="http://com.ibm/portal/portlets/ns">
x:city</qname>
		</supported-publishing-event>
	</portlet>
	<event-definition>
		<qname xmlns:x="http://com.ibm/portal/portlets/ns">x:city</qname>
		<value-type>java.lang.String</value-type>
	</event-definition>

それぞれのイベントには完全な QName を指定することも、あるいは多くのイベントに同じ名前空間を使用している場合には、その名前空間をデフォルト名前空間として宣言し、各イベントのローカル部分だけを指定することもできます。

名前の他に指定しなければならないのは、イベント・ペイロードの型です。これを指定することによって、ポートレットが異なるクラスローダーを使用している場合や、WSRP (Web Services for Remote Portlets) を使ってリモート・ポートレットとして実行されている場合でも、ポータル・アプリケーションがイベント・ペイロードをシリアライズ/デシリアライズできるようにします。したがって、イベント・ペイロードの型に関する要件は単純な型 (String) であるか、または Java および JAXB (Java Architecture for XML Binding) でシリアライズ可能な型のいずれかでなければなりません。JAXB によるシリアライズも必要な理由は、ポートレットが WSRP を使用して実行中のリモート・ポートレットにイベントを送信しなければならない場合があるからです。そのようなリモート・ポートレットは Java で作成されていない可能性があるため、Java によるシリアライズよりも一般的なメカニズムが必要になります。JAXB でなら、Java オブジェクトを XML にシリアライズすることができるので、Java オブジェクトに適切なアノテーションを付けるだけで済みます。

かつてはこの詳細が難しい部分でしたが、現在は作業がより単純化され、イベントの送受信はポートレットを介して行われるようになりました。イベントを送信するには、ActionResponse または EventResponse で setEvent メソッドを使います。イベントを受信するには、GenericPortlet を継承するか、あるいは EventPortlet インターフェースを自分で実装します。GenericPortlet を継承すると、イベントの操作、アクションの処理、そして特定のポートレット・モードのレンダリングといったことを行うための特殊なメソッドをアノテーションで表すことができるというメリットもあります。

リスト 2 はその一例です。


リスト 2. アノテーションを使用したイベント処理の例
                
	@ProcessEvent(qname="{http://com.ibm/portal/portlets/ns}city")
	public void cityEvent(EventRequest request, EventResponse response )
                                      throws IOException, PortletException 
	{	
		Event ev = request.getEvent();
		if ( ev.getValue().equals("Orlando") ) {
			….
		}
		…
	}
	

共通レンダリング・パラメーター

共通レンダリング・パラメーターを使用すると、異なるポートレット間、または IBM WebSphere Portal でのテーマやポータル・ページといった成果物の間で要求パラメーターを共有することができます。共通レンダリング・パラメーターの定義は、イベントの定義と非常によく似ています。つまり、固有の QName とオプションのエイリアス名を portlet.xml デプロイメント記述子に指定するということです。QName とエイリアス名は、複数の異なるポートレットの共通レンダリング・パラメーターを結び付けるために使用します。これらの共通レンダリング・パラメーターは、すべての参加ポートレットが値を設定および取得することのできる共通ストレージ域として考えることができます。


図 3. 共通レンダリング・パラメーターの連携例

図 3 に示す例では、ナビゲーター・ポートレット (Navigator portlet) とコンテンツ・ポートレット (Content portlet) が同じ共通パラメーター docid を共有しています。ナビゲーター・ポートレットで docid を document3 に設定するリンクをクリックすると、コンテンツ・ポートレットがその文書の内容を表示します。これは、コンテンツ・ポートレットのビュー状態 (現行文書の ID) が外部からの影響によって変更されたためです。共通レンダリング・パラメーターは、WebSphere Portal 実装での場合と同じく URL に保管できるため、ページにブックマークを設定してブラウザーの Back および Forward ボタンを使用することができます。

これらの共通レンダリング・パラメーターにポートレットがアクセスするには、どうすればよいのでしょうか。そのポートレット自体のなかで共通レンダリング・パラメーターにアクセスするためのメソッドは、Java Portlet Specification のバージョン 1.0 で定義された通常のレンダリング・パラメーターを操作するときに使うメソッドと同じです。これらのメソッドがキーとして使用するのは String だけで、QNames は使用しないことから、ID を portlet.xml に定義し、その ID を使ってポートレット・コード内で共通レンダリング・パラメーターを指定できるようにしなければなりません。

ポートレット・コードが実際に、レンダリング・パラメーターが共通なのか、あるいは専用なのかを変更しなければならないことはほとんどないので、portlet.xml のビュー状態情報で外部ソースから妥当に設定できる部分を共通パラメーターとして宣言するだけで、ポートレットを連携させることができるようになります。コード内の共通レンダリング・パラメーターにのみアクセスする必要がある場合は、要求で getPublicParameterMap メソッドを併せて使用してください。


リソース供給

Java Portlet Specification の最初のバージョンでは、動的に生成されるリソースをポートレットから直接供給することはできなかったため、サーブレットを追加してリソースを供給させなければなりませんでした。この制約には以下のようないくつかの欠点がありました。

  • ポートレットのコンテキストを利用することができません。つまり、レンダリング・パラメーター、ポートレット・モード、ウィンドウ状態、ポートレット・プリファレンス、ポートレット・セッションなどにアクセスできないということです。

  • サーブレットで生成される URL はポータルのスコープ内に入らないため、ポータル・エクスペリエンスがそのままの状態になります。その上、ポータルの URL に蓄積されたポータル・ページの最新の状態が失われてしまいます。

  • サーブレットはポータルのアクセス制御の対象外として実行されるため、別途セキュアにしなければなりません。

リソースをサーブレットから供給する場合の 1 つの利点は、例えば大量のメディア・ストリームを供給するときなどに、ポータル・フレームワークを追加した上で、そのフレームワークをとおして要求を行う必要がないので、オーバーヘッドが少ないという点です。

Java Portlet Specification のバージョン 2.0 では、新しい URL タイプとしてリソース URL を導入しています。リソース URL はライフサイクル・メソッド、serveResource を ResourceServingPortlet インターフェースで呼び出すため、これを利用してポートレット内で直接、動的リソースを作成することが可能です。

リソース URL

リソース URL は新しいライフサイクル・メソッド、serveResource を呼び出す必要があります。まずは、この新しいリソース URL の詳細から説明することにします。

リソース URL を作成するには、他のポートレット URL と同様、RenderResponse や ResourceResponse で createResourceURL メソッドを使用します。以下に示すのはこのメソッドの使用例です。

ResourceURL url = response.createResourceURL();

これで、他のポートレット URL の場合と同じように URL にパラメーターを設定することができます。これらのパラメーターは、serveResource 呼び出しで受け取ります。注意する点として、ResourceURL では新しいレンダリング・パラメーター、ポートレット・モード、ウィンドウ状態を設定することはできません。この制約の原因となっているのは、serveResource 呼び出しは新規ポータル・ページを完全に生成するのではなく、serveResource の応答を返すという点です。したがってポータルでは、これらの情報がエンコードされる可能性のあるページの、他の部分を更新することができません。例えば WebSphere Portal では、すべての URL に上記の情報が含まれているため、更新は必須です。

リソース URL には、リソースを明確に識別するための追加リソース ID を設定することもできます。GenericPortlet を継承すると、GenericPortlet は設定されたリソース ID を serveResource呼び出しに対して転送しようと試みます。ID には、以下に示すようにリソースへのパスを設定することができます。

url.setResourceID("WEB-INF/jsp/xmlcontent.jspx");

この場合、GenericPortlet は指定された JSP に自動的にディスパッチすることになるため、その JSP にポートレット・タグ・ライブラリーを組み込むことによってポートレットの状態情報を利用できるようになります。ただし、ポートレット WAR にパッケージ化された GIF ファイルなどの静的リソースに関しては、以下のように静的リソース URL を使用して参照するのが通常です。

String url = response.encodeURL(request.getContextPath()+"/icons/myigif.gif");

これは、ポートレットの serveResource メソッドを使用して静的リソースを供給すると、不必要なパフォーマンス・オーバーヘッドが生じるためです。

リソース供給

リソース URL によって行われた serveResource 呼び出しを受け取るには、新しい ResourceServingPortlet を実装するか、あるいはこの新規インターフェースを実装済みの GenericPortlet を継承する必要があります。serveResource 呼び出しで取得するのは、特定のリソースに関する要求/応答のペアです。

リソース要求にはレンダリング要求と同様のメソッドがありますが、リソース要求はそれに加え、データをアップロードするためのアクセスも可能にします。リソース要求では、パラメーターを操作することにより以下の 3 種類のパラメーターにアクセスできるようになります。

  • リソース URL に設定されたリソース・パラメーター。これらのパラメーターは該当するリソース呼び出しにのみ使用可能で、getPrivateParameterMap 呼び出しによってアクセスします。

  • ポートレットの専用レンダリング・パラメーター。getPrivateRenderParameterMap 呼び出しによってアクセスします。

  • 共通レンダリング・パラメーター。getPublicParameterMap 呼び出しによってアクセスします。

通常のパラメーター・アクセス・メソッドは、上記の 3 つのパラメーター・マップがマージされたセットを返します。同じキーを持つパラメーターはまずマージされたリソース・パラメーターの値を取得し、次に、パラメーター・キーが portlet.xml で共通レンダリング・パラメーターとして宣言されているかどうかによって専用または共通レンダリング・パラメーターの値を取得します。

リソース URL では、GET だけでなく、レンダリングの場合と同じくすべての HTTP メソッドを利用することができます。これはつまり、serveResource 呼び出しの中で、状態を変更するための POST や DELETE などのメソッドを使用できるということです。ただし変更対象は、ポートレットの専用状態 (ポートレット・スコープのセッション・データおよびポートレット・プリファレンス) に限られます。他のポートレットに影響を及ぼす状態は、変更してはなりません。ポータル・フレームワークは serveResource 呼び出しに対してポータル・ページの他の部分を更新することができないため、ページ・レベルでは状態の更新が表示されない可能性があるからです。

注: レンダリング・パラメーター、ポートレット・モード、およびウィンドウ状態は変更してはならないカテゴリーに分類されます。WebSphere Portal などの一部のポータル実装では、この情報を URL に保管してブックマーク機能とブラウザーでの Back および Forward ボタンの使用をサポートするからです。このようなポータル実装では、レンダリング・パラメーターを変更するためには例えばページ上のすべての URL を変更しなければならなくなることを意味します。しかし、クライアント・サイドでの応答データの処理は完全にポートレットが行うため、ポータルでそのような変更を実行することはできません。

serveResource から返されるマークアップはポータル・フレームワークの他のマークアップとは集約されないことから、リソース応答では出力ストリームを完全に制御することができます。その一例として、ポートレットは HTTP ステータス・コードを設定することができます。

リソースのキャッシュ・レベル

リソース供給には多種多様な使用事例があるため、serveResource 呼び出しのキャッシュ機能にもさまざまな要件があります。リソース要求の HTTP キャッシュの振る舞いを操作するには、ResourceURL の setCacheability メソッドを使用します。このメソッドは、ターゲット serveResource 呼び出しに必要な情報の量についてポータルにヒントを与えます。このヒントによってポータルは関連性のないデータを URL から削除できるようになるので、URL はより安定し、リソース要求に対する HTTP キャッシュのヒット率が向上することになります。一方、リソース応答のキャッシュ機能を設定することに意味があるのは、返される応答に HTTP キャッシュ・ヘッダーを指定して、応答をキャッシュできるようにする場合のみです。このように指定する場合に最低限必要となるのは、response.getCacheControl().setExpirationTime() による有効期限の設定です。返される内容がユーザーに固有でない場合は、さらにキャッシュ制御を共通スコープに設定する必要もあります。

JSR 286 では以下のシナリオをサポートします。

  • 対話動作の状態に関わらず、完全にキャッシュ可能なリソース。その一例はプログラムによって生成される SVG ビューです。このビューはバックエンド・アクセスに関するポートレット・プリファレンスに依存します。serveResource 呼び出しを完全にキャッシュ可能な呼び出しとするには、リソース URL でキャッシュ機能を FULL に設定してください。この設定により、ポータルはページおよびページ上のポートレットの対話動作の状態が含まれない URL を生成することが可能になります。したがってブラウザーは、ユーザーが少なくとも最新のページと対話動作をしている限りは、serveResource 呼び出しで返されたマークアップをキャッシュに入れておくことできます。

    ページやページ上のポートレットの状態をエンコードしないと、ポートレットの現行のレンダリング・パラメーター、ポートレット・モード、あるいはウィンドウ状態を取得しても当てにできなくなります。このような状態情報の欠如により、serveResource の出力が制限されることに注意してください。つまり、出力に含められるのは完全にキャッシュ可能なリソース URL のみで、アクションまたはレンダリング URL は含められないということです。

  • 共有された静的リソースなど、完全にキャッシュ可能な共有リソース。その一例は、JavaScript ライブラリーです。URL に SHARED プロパティーを追加し、その値として共有リソースの名前を設定することで、キャッシュ機能を FULL に設定したキャッシュ可能なリソース供給 URL を共有にすることができます。この名前は、リソースを一意に識別する QName にします (例えば、{http://dojotoolkit.org/V1.0}dojo.js)。

    共有プロパティーを設定すると、ポータルが使用できる共有リソースのバージョンがページごとに 1 つだけとなります。ただし、ポータルがリソースをまだロードしていない場合に備え、ポートレットがこれらのリソースを WAR ファイルにパッケージ化しなければならないことには変わりありません。

    ポートレットが提供しなくてもよいポータル・リソースについては、ベンダーがそれぞれ特有のものを使用します。例えば、WebSphere Portal では ResourceURLAccessor を使用してリソース URL を作成することができます。

  • ポートレットの対話動作の状態に応じてポートレット・レベルでキャッシュ可能なリソース。その一例は、動的に生成される現行ポートレット・ビューの PDF 出力です。レンダリング・パラメーターやポートレット・モード、あるいはウィンドウ状態などのポートレット状態にはアクセスする必要がある一方、応答やその後の応答にはアクションまたはレンダリング URL を含めないという場合には、キャッシュ機能レベルを PORTLET に設定してください。このレベルでは、ポートレットとの対話動作が行われるたびにリソースを最新の状態にできると同時に、ページ上のその他のポートレットと対話動作をする際にキャッシュに入れられたリソースを使用することもできます。

    アクションまたはレンダリング URL を提供するリソースなど、ページ・レベルでキャッシュ可能なリソース。その一例は、Ajax 呼び出しで返されたマークアップです。キャッシュ機能のレベルは、デフォルトで PAGE に設定されます。serveResource メソッドには一切制限が課せられないため、アクション・リンクやレンダリング・リンクを生成することができます。この設定では、ポータル・ページと相互作用を行ってページの状態の一部を変更しても、リソースをキャッシュすることはできません。

以上をまとめると、これらのキャッシュ機能レベルにより、キャッシュ機能に関する可能な限りのヒントをランタイムに提供することができます。ランタイムがリソースのキャッシュ機能を強化する方法は他にもあります。例えば、クライアントで状態変更を追跡し、状態変更によって影響を受ける部分のみを再レンダリングするなどです。その一例として、WebSphere Portal V6.1 ベータ版での Client-side Aggregation テーマが挙げられます。


Ajax の使用

Java Portlet Specification のバージョン 1.0 では、Ajax による使用事例のサポートはかなり最小限に抑えられていました。バージョン 1.0 で可能だったのは、Ajax フラグメントを供給する追加サーブレットを提供することだけです。しかも、このサーブレットは図 4 を見るとわかるようにポータル・フレームワークを介してではなく、直接アドレス指定されるため、ポートレット状態が提供されることも、ポータルのセキュリティー保護を受けることもありませんでした。また、ポートレットとサーブレットの間でデータを共有するには、アプリケーション・スコープ・セッションを使用するか、サーブレットを呼び出す URL でパラメーターを使うという手段しかありませんでした。


図 4. JSR 168 での Ajax ソリューション: 追加サーブレットによる Ajax データの供給

それでは、新しい Java Portlet Specification では、どのように Ajax による使用事例をサポートするようになっているのでしょうか。

これまで説明してきたように、JSR 286 はポートレットを使って直接リソースを供給する手段を提供します。したがって、XmlHttpRequest を ResourceURL に対して発行すれば、サーバー側ではレンダリング・パラメーター、ポートレット・モード、ウィンドウ状態、ポートレット・プリファレンス、そしてポートレット・セッションなどのポートレット・コンテキストに完全にアクセスすることができます。

また、serveResource 呼び出しでは以下の状態変更を行えることも説明しました。

  • ポートレット・プリファレンスの変更
  • ポートレット・セッション・スコープ内のデータの変更

今や、ユーザー・インターフェースの応答性を向上させる非同期更新などの追加の使用事例を実装することが可能になっています。


図 5. JSR 286 での Ajax ソリューション: ポートレットから直接供給する Ajax データ

図 5 に示しているのは、JSR 286 でのソリューションです。この図からわかるように、Ajax 呼び出しはポータル・サーブレットを通過するため、ポータルの制御対象となります。さらに、リソース URL を使用すれば、JavaScript ライブラリーへのリンクを提供することも可能です。


サーブレット・プログラミング・モデルとのギャップの解消

Java Portlet Specification の最初のバージョンでは、ポートレット・プログラミング・モデルはいくつかの領域に制限されており、サーブレット・プログラミング・モデルとは対照的なものでした。この制限が設けられた理由は、ポートレットはあるページに集約されますが、その一方で、ページ上の唯一のコンポーネントであることが前提となる概念は、どれも簡単にはポートレット・プログラミング・モデルに適用できないからです。

バージョン 2.0 では上記の問題に対処し、ソリューションを提供しています。そのため、ポートレット・プログラミング・モデルはサーブレット・プログラミング・モデルとほとんど同じ機能を備えるようになっただけでなく、さらにポートレット固有の拡張機能も持つようになりました。

クッキー、文書見出しセクションの要素、および HTTP ヘッダー

Java Portlet Specification の最初のバージョンでは、ポートレットはそのウィンドウ内に含まれないポータル・ページの部分には寄与することができませんでした。バージョン 2.0 では、クッキー、文書見出しセクションの要素 (例えば、meta、link、style などの HTML 要素など)、そして HTTP ヘッダー (アプリケーション固有の Pragma ヘッダーなど) を設定できるようになっています。

2 つの部分で構成されるレンダリング・ライフサイクル呼び出し
Java Portlet Specification のバージョン 1.0 で現行のポートレット・ウィンドウ外部にあるコンテンツを提供する際に直面した問題を克服するため、バージョン 2.0 では以下の 2 つの部分で構成されるレンダリング呼び出しを追加しています。

  1. RENDER_HEADER 部分では、ポートレットが現行のポートレット・ウィンドウ外部にあるコンテンツ (ポートレット・タイトル、次に設定可能なポートレット・モード、クッキー、文書見出しセクションの要素、HTTP ヘッダーなど) を返すことができます。

  2. RENDER_MARKUP 部分では、ポートレットが通常のマークアップを返すことができます。

この 2 つの部分が必要になる理由は、WebSphere Portal などの一部のポータル実装では、バッファリングのオーバーヘッドをなくすためにページおよびポートレット出力をクライアントに直接配信するからです。この場合、文書の見出しセクションには、すでに書き込まれているため、ポートレットのレンダリングでは見出しセクションに何も追加することができません。したがって、見出しセクションに寄与したり、ポートレット・タイトルを設定したりする必要があるポートレットについては、リスト 3 に示すポートレット・デプロイメント記述子の設定によって 2 つの部分で構成されるレンダリング機能を有効にしてください。


リスト 3. 2 つの部分で構成されるレンダリング・ライフサイクルの有効化
                
<portlet>
…
		<container-runtime-option>
			<name>javax.portlet.renderHeaders</name>
			<value>true</value>
		</container-runtime-option>
	</portlet>
	

GenericPortlet を継承して GenericPortlet が提供する getTitle や getHeaders などのメソッドをオーバーライドすることで、2 つの部分で構成されるレンダリングは簡単に実装することができます。こうしておけば、残りの作業は、GenericPortlet クラスが行ってくれます。

クッキーの使用
それぞれのライフサイクル・メソッド (processAction、processEvent、render、serveResource) の応答でクッキーを設定するには、以下のコードを使用します。

response.addProperty(javax.servlet.http.Cookie cookie)

すると、以下のコードによって、すべてのライフサイクル・メソッドでクッキーにアクセスできるようになります。

request.getCookies()

ポートレットでのクッキーの使用方法は、サーブレットで使用する場合と以下の点で異なります。

  • 前述の通り、クッキーを render メソッドで設定するには renderHeaders オプションを有効に設定し、例えば GenericPortlet の doHeaders() をオーバーライドして RENDER_HEADERS 部分でクッキーを設定する必要があります。

  • クッキーはポータル・サーバーで保管されるか、(ポートレットが WSRP を介してリモート・ポートレットとして実行されている場合には) 異なる名前空間に保管されるため、クライアントではクッキーにアクセスできない可能性があります。

  • クッキーが異なるポートレット間で共有できるという保証はありません。

要求ディスパッチャーの include および forward

Java Portlet Specification の最初のバージョンでは、レンダリング・ライフサイクル呼び出しからサーブレットまたは JSP をインクルード (include) するという選択肢しかありませんでした。バージョン 2.0 では、forward と include をすべてのライフサイクル・メソッドで使えるようになっています。

この追加内容が意味することは、サーブレットで作成されたアクションまたはイベント・ロジックへのディスパッチが可能になったこと、あるいはリソース供給で ResourceURL に設定したリソース ID に転送しようとしている場合には転送を行えるということです。

サーブレット・ライフサイクル・リスナーの利用

PortletSession は HttpSession のファサードとなっているため、Java Portlet Specification のバージョン 1.0 ではサーブレットの HttpSession リスナーを PortletSession に対して使用することができます。バージョン 2.0 ではこのリストを Java Servlet Specification V2.5 に定義されたすべてのサーブレット・ライフサイクル・リスナーにまで拡張し、ポートレットとサーブレットのオブジェクト間の対応付けを強化しています。

  • javax.servlet.ServletContextListener。サーブレットのコンテキストとそれに対応するポートレットのコンテキストに関する通知に使用します。

  • javax.servlet.ServletContextAttributeListener。サーブレットのコンテキストまたは対応するポートレットのコンテキストに含まれる属性に関する通知に使用します。

  • javax.servlet.http.HttpSessionActivationListener。HTTPSession または対応する PortletSession の有効化または無効化に関する通知に使用します。

  • javax.servlet.http.HttpSessionAttributeListener。HTTPSession または対応する PortletSession の属性に関する通知に使用します。

  • javax.servlet.http.HttpSessionBindingListener。HTTPSession または対応する PortletSession へのオブジェクト・バインディングに関する通知に使用します。

  • javax.servlet.ServletRequestListener。HTTPServletRequest またはそれに対応する現行 Web アプリケーションのポートレット要求への変更に関する通知に使用します。

  • javax.servlet.ServletRequestAttributeEvent。HTTPServletRequest またはそれに対応する現行 Web アプリケーションのポートレット要求の属性への変更に関する通知に使用します。

サーブレット要求リスナーは、要求属性 javax.portlet.lifecycle_phase を調べることにより、サーブレットを対象とした単純なサーブレット要求と、ポートレットを対象としたラップ・サーブレット要求とを区別します。この属性はポートレットを対象とした要求に設定され、この要求のポートレット・ライフサイクルの現行フェーズを示します。

これらすべてのライフサイクル・リスナーへのアクセスは、ライフサイクルに関するオブジェクトを管理するための多数のフック・ポイントを提供するため、多くのフレームワークにとって素晴らしい機能となります。ただし要求ライフサイクル・リスナーの場合をはじめ、これらのフック・ポイントにはコストが伴うため、要求ごとに顕著な処理オーバーヘッドが追加されます。したがって、慎重に使用する必要があります。


JSR 286 の拡張

JSR 286 のエキスパート・グループは JSR 286 を拡張できるように取り組んできたため、JSR 286 をベースとしたフレームワークには、他への影響がなくコンテナーに依存しない方法で、以下に示すようなフィーチャーおよび機能を追加することができます。

  • サーブレット・ライフサイクル・リスナー
  • 要求/応答をラップするためのポートレット・フィルター
  • 出力ストリームに書き込まれる前に URL を操作するための PortletURL リスナー
  • ポートレットがポートレット自体のポートレット・モードを提供できるようにするポートレット管理モード

拡張ポイントによっては、コンテナーがその拡張ポイントをサポートしなければポートレットが拡張を利用できない場合があります。これに該当する拡張ポイントは以下のとおりです。

  • Java Portlet Specification のバージョン 1.0 から引き継がれた、拡張プロパティーを要求または応答に設定するための要求/応答プロパティー。バージョン 2.0 では、事前設定キャッシュ・プロパティーをはじめ、新たな仕様が定義された複数の拡張プロパティーが追加されています。

  • PortletURL を拡張するための PortletURL プロパティー。例えば、ResourceURL に事前に定義された SHARED プロパティーなどです。

  • コンテナーに追加された動作を利用するためのコンテナー・ランタイム・オプション。これには 2 つの部分で構成されるレンダリングなどがあります。

以降のセクションで、Java Portlet Specification を拡張する新しい機能について詳しく説明します。

ポートレット・フィルター

新しいポートレット・フィルター機能により、ポートレットのあらゆるライフサイクル呼び出しにフィルターを接続することができます。共通装飾子パターンに続くフィルターは、プリプロセッシングとポストプロセッシングを実行し、ポートレットに渡される要求および応答オブジェクトを変更、またはラップすることができます。以下に示すのは、ポートレット・フィルターの典型的なアプリケーションに含めることができる動作です。

  • 追加ソースからの情報を属性またはパラメーターとしてポートレットに渡します。
  • セキュリティー強化またはマークアップ準拠のために出力をフィルタリングします。
  • 診断情報の収集を行います。
  • Web アプリケーション・フレームワーク間の接続をします。

一例として、WebSphere Portal V6.1 はフィルターによる方法を使って、クライアント・サイドのクリックによるアクションについてのセマンティック情報を追加することで、ポートレットのマークアップを増強します。

ポートレット・フィルターのプログラミング・モデルは、以下のようにサーブレット・フィルター・モデルを基にモデル化されます。

  1. デプロイメント記述子にフィルターを定義します。この定義には <filter> 要素を使用し、この要素にフィルターを適用するライフサイクル呼び出しを指定する必要があります。

  2. フィルターに、対応する Filter インターフェースを実装します。複数のライフサイクル・エントリーを記載して、現在使っているクラスで複数の Filter インターフェースを実装することもできます。

  3. filter-mapping 要素に、フィルターを適用するポートレットを記述します (アプリケーションに含まれるすべてのポートレットに適用する場合には、アスタリスクをワイルドカードとして使用することもできます)。

デプロイメント記述子での filter-mapping 要素の順序によって、ポートレットに適用されるフィルターの順序が決まります。リスト 4 は、デプロイメント記述子の入力例です。


リスト 4. ポートレット・フィルター定義の例
                
// filter declaration
<filter>
		<filter-name>PortletFilter</filter-name>
		<filter-class>com.example.PortletFilter</filter-class>
		<lifecycle>RENDER</lifecycle>
</filter>

// filter mapping		
<filter-mapping>
		<filter-name>PortletFilter</filter-name>
		<portlet-name>MyPortlet</portlet-name>
</filter-mapping>

実行時には、すべてのフィルターからなるフィルター・チェーンがポートレットに適用されます。それぞれのフィルターが現行の要求と応答、あるいは前のフィルターによって作成されたラップ・バージョン、そしてフィルター・チェーンを取得します。プリプロセッシングが完了すると、フィルター実装は要求処理を終了するか、またはフィルター・チェーンでの次の要素を呼び出して受け取った要求と応答、または追加ラッパーのいずれかに渡します。フィルター・チェーンの最後の要素は、ポートレット自体です。

リスト 5 に例として示すフィルターは、プリプロセッシングとポストプロセッシングを行い、ラップした要求をポートレットに提供します。


リスト 5. フィルター実装の例
                
public class PortletFilter implements RenderFilter {

  		public void doFilter(RenderRequest req, 
         RenderResponse resp, FilterChain chain) throws .. {
			PrintWriter pw = resp.getWriter();
			pw.write("Pre-processing");

			MyRenderResponseWrapper resWrapper = 
					new MyRenderResponseWrapper(res);
			chain.doFilter(req, resWraper);

			pw.write("Post-processing");
  		}
}

ラッパーを使用する場合は、ラッパー・パターンに従い、オーバーライドできるのは既存の振る舞いだけであることに注意してください。ラッパーに新しいメソッドを追加することはできません。他にも存在を把握していないラッパーがチェーンに含まれていると、ポートレットが新しいメソッドにアクセスできないためです。ポートレットに追加機能を提供する必要がある場合は、その機能にアクセスするオブジェクトを要求属性として設定してください。

ポートレット URL リスナー

場合によっては、ポートレット URL での特定プロパティーの設定を一元管理したり、既存のポートレットのポートレット URL の作成を強化したりしなければならないことがあります。その一例が、リソースと共有 ID のマッピングを実装する場合です。このマッピングをいったんリスナーに実装すると、リスナーはリソース URL の対象が共有 QName のあるリソースであるかどうかをチェックしてから、対応する QName を使って SHARED プロパティーを設定します。このような使用事例に備え、JSR 286 仕様ではポートレット URL リスナーを用意しています。

ポートレット URL リスナーは、ポートレット・デプロイメント記述子の listener 要素に登録する必要があります。また、使用しているクラスが PortalURLGenerationListener インターフェースを実装する必要もあります。このインターフェースが、ポートレット URL の各タイプ (アクション、レンダリング、リソース) のコールバック・メソッドを定義します。

ポートレット管理モード

JSR 168 でポートレットが利用できたのは、そのポートレットを実行するポータル・フレームワークがサポートするポートレット・モードだけでした。しかし一部の使用事例では、ポートレットは、ポータルがサポートするポートレット・モードと同じルック・アンド・フィールを持ったポートレット固有の機能を提供しなければならない場合もあります (ポートレット・ウィンドウでコンテキスト・メニューを使用するなど)。そのような例としては、ショッピング・カート内にあるすべてのエントリーをリストする ShowShoppingCart ポートレット・モードがあります。

上記のような使用事例をサポートするために JSR 286 で導入しているのが、ポートレット管理モードです。ポートレット管理モードはポータルには認識されませんが、ポートレット自体によって管理されます。ポートレットは、リスト 6 に示すコードをポートレット・デプロイメント記述子に記述することで、ポートレット管理モードを宣言することができます。


リスト 6. カスタム・ポートレット管理モードの定義
                
 	<custom-portlet-mode>
   		<description>Show shopping cart</description>
<portlet-mode>ShowShoppingCart</portlet-mode>
		 <portal-managed>false</portal-managed>
  	</custom-portlet-mode>

ここで重要な点は、ポータル管理 (potal-managed) モードを false に設定することです。この設定により、ポータルはこのモードを、ポートレットにプリファレンスを指定する方法やアクセス制御の観点をはじめとするすべての側面に関して標準 View モードと同じように扱うことになります。ポートレットがこのモードに切り替えられるようにするには、ポータルは UI コントロールを提供しなければなりません。ポートレットは、以下のようにリソース・バンドル・エントリーを使用して、修飾用のローカル名を定義することができます。

javax.portlet.app.custom-portlet-mode.<name>.decoration-name.

ポータルはポートレット管理モードの特定のセマンティクスを認識しないため、ポートレット管理モードに切り替えて修飾を表示するのが相応しい場合とそうでない場合とを判断することができません。そのため JSR 286 仕様では、ポータルがレンダリング応答の一部として UI コントロールを表示しなければならないポートレット・モードを、setNextPossiblePortletModes メソッドで指示できるようにしています。GenericPortlet を使用する場合は getNextPossiblePortletModes をオーバーライドできるため、GenericPortlet が RENDER_HEADER 部分でこれらの設定を行ってくれます。注意する点として、このモードのリストは応答ごとに設定しなければなりません。また、次のセクションで説明するコンテナー・ランタイム・オプションの 1 つ、javax.portlet.renderHeaders を有効にしなければならないことにも注意してください。

コンテナー・ランタイム・オプション

コンテナー・ランタイム・オプションを使用すると、ポートレットが特定のオプションをポートレット・コンテナーに指定することができます。これらのオプションは、Java Portlet Specification で定義されたデフォルトの振る舞いを変更するか、あるいは振る舞いを追加するためのものです。このようなコンテナー・ランタイムの設定例としては、すでに renderHeaders オプションを紹介しています。JSR 286 が定義している一連のコンテナー・ランタイム・オプションは、actionScopedRequestAttributes を除き、すべて実装は任意に選択可能となっています。これらのオプションに加え、特定のポートレット・コンテナー実装が独自のオプションを用意している場合もあります。

ポートレットに特定のコンテナー・ランタイム・オプションが必要であることを示すには、デプロイメント記述子にリスト 7 のコードを記述します。


リスト 7. コンテナー・ランタイム・オプションのテンプレート
                
<portlet>
…
		<container-runtime-option>
			<name>NAME_OF_THE_OPTION</name>
			<value>optionally you can have one or more parameters</value>
		</container-runtime-option>
	</portlet>

JSR 286 で事前定義されているオプションは以下のとおりです。

  • javax.portlet.escapeXml。URL が XML でエスケープされないことを前提として作成した JSR 168 ポートレットを JSR 286 ポートレットにマイグレーションしてある場合には、それぞれのデフォルトに対してポートレット URL の XML エスケープを無効にすることができます。

  • javax.portlet.renderHeaders。2 つの部分で構成されるレンダリング・メカニズムを有効にして、ストリーミング・ベースのポータル実装でヘッダーを設定できるようにします。

  • javax.portlet.servletDefaultSessionScope。アプリケーション・スコープからポートレット・スコープへの forward または include によってポートレットから呼び出されるサーブレットあるいは JSP に提供するセッション・オブジェクトのデフォルト・スコープを変更することができます。デフォルト・スコープを変更して、これらのリソースがポートレットおよびサーブレット API を使用して同じセッション・データにアクセスするように設定すると、JSP がサーブレット・ベースの Web フレームワークを対象に作成されている場合に特に役立ちます。

  • javax.portlet.actionScopedRequestAttributes。Web フレームワークが要求によってアクションまたはイベント・フェーズからレンダリング・フェーズに複合オブジェクトを渡せるようにします。この場合、アクション・セマンティック (Action URL またはイベントによって呼び出される可能性があるアクション) を使用した次の要求が行われるまで、これらの属性へのアクセスが可能となります。この機能をポートレット・コンテナーが実装するには、おそらくこれらの属性をセッションに保管するという方法になるはずです。それによってパフォーマンスの劣化につながる恐れがあるため、この機能を使用するのはやむをえない場合だけにしてください。このオプションは、JSR 286 がすべてのコンテナーでサポートすることを要求している唯一のオプションです。

コンテナー・ランタイム・オプションを使用する場合、ポートレットがデプロイされているコンテナーがそのオプションをサポートしていなければ、コンテナーはポートレットのデプロイメントを拒否する場合があります。多種多様なポートレット・コンテナー実装で実行可能なポートレットを開発しようとしている場合には、これらのオプションは慎重に使用してください。


後方互換性

Java Portlet Specification V2.0 は、V1.0 とのバイナリー・コードの互換性を損なわずに、すべての API メソッドで矛盾のない振る舞いを維持するように設計されています。この設計上のポイントは、V1.0 仕様に対して作成されたすべてのポートレットは V2.0 コンテナーでも何も変わることなく実行されるはずだということを意味します。ただし、以下に挙げた振る舞いに関する多少の変更はこの規則の例外です。これらの変更がポートレットを壊す可能性は、通常考えられません。

  • getWriter または getOutputstream を呼び出す前に RenderResponse.setContentType を呼び出す必要はなくなりました。V2.0 では、事前にコンテンツ・タイプを設定せずに getWriter または getOutputstream を呼び出しても IllegalStateException という結果にはなりません。

  • getProtocol を呼び出すと、インクルードされているサーブレット/JSP によって、今までのようにヌルが返ってくる代わりに、V2.0 では HTTP/1.1 が返されるようになっています。

このような後方互換性に関する記述には、V2.0 で新しいエントリーが追加されたけれども既存のエントリーは変更していない、デプロイメント記述子も含まれています。つまり、ポートレットのデプロイメント記述子の中でスキーマを参照している 1 行を、V2.0 の新しいポートレットのデプロイメント記述子のスキーマを参照するように変更するだけで、ほとんどの JSR 168 ポートレットを JSR 286 ポートレットに変換できることを意味します。

このセクションでは細かい追加内容について説明します。これらの追加によって作業は簡易化されるとともに、API で使用される新規 Java 5 の機能や新しいキャッシュ機能、そしてアクセス可能なランタイム ID での変更やタグ・ライブラリーの追加などの新しい使用事例をサポートできるようになっています。

Java 5 の機能

JSR 286 では以下の新しい Java 5 の機能を活用します。

  • Generics の使用
  • すべてのユーザー・プロファイル属性に対する新規 enum クラスの導入

GenericPortlet でも、ライフサイクル呼び出しから特定のハンドラー・コードへのディスパッチを簡易化する以下のアノテーションを使用できるようになっています。

  • ProcessAction。このアノテーションを使用して、特定のアクションを処理しなければならないメソッドにアノテーションを付けることができます。ディスパッチを可能にするには、Action URL に javax.portlet.action パラメーターを含める必要があります。
  • ProcessEvent。特定のイベントを処理しなければならないメソッドにアノテーションを付けるには、このアノテーションを使用します。
  • RenderMode。特定のポートレット・モードをレンダリングする必要があるメソッドにアノテーションを付けるには、このアノテーションを使用します。

リスト 8 に、ProcessAction アノテーションの使い方を示します。


リスト 8. ProcessAction アノテーションの利用方法
                
    // generating the URL
    PortletURL url = response.createActionURL();
    url.setParameter(ActionRequest.ACTION_NAME, "actionA");
    url.write(output);

    // method for handling the action
    @ProcessAction(name="actionA")
    void processMyActionA(ActionRequest req, ActionResponse resp)
    throws PortletException, java.io.IOException; {
    …
    }

一部のポータル実装はまだ Java 1.4 に依存しているので、JSR 286 に追加された機能は Java 1.4ベースのプラットフォームで簡単に削除できるものに限られています。Java 1.4 ベースのプラットフォームの場合、JSP 286 API の JAR ファイルは前回の機能が削除された Java 1.4 準拠の設定でコンパイルされています。

新規キャッシュ機能

JSR 286 が追加する以下の新しいキャッシュ機能は、ポートレットをより拡張しやすくします。

  • ユーザー間で共有されているキャッシュ・エントリーにマークを付ける共通キャッシュ・スコープ
  • ポートレット単位での複数キャッシュ・ビューのサポート
  • 有効期限が切れたキャッシュ・エントリーを確認するための妥当性検査に基づくキャッシュ

JSR 168 では、すべてのキャッシュ・エントリーは各ユーザーに専用のものでした。つまり、ポートレットがカスタマイズ不可能で、各ユーザーに応じて異なるコンテンツを表示しない場合には (その日のニュースを提供するポートレットなどの場合)、すべてのエントリーが同じであったとしてもユーザーごとにキャッシュ・エントリーを取得することになります。JSR 286 では、デプロイメント記述子で cache-scope 要素を使うことにより、ポートレット・コンテナーにキャッシュ・エントリーが共有可能であることを通知できるようになりました。そのためリスト 9 に示すように、このようなポートレットのメモリー・フットプリントを大幅に縮小することができます。


リスト 9. 共通キャッシュ・スコープの例
                <portlet>
    		...
      		<expiration-cache>60</expiration-cache>
		<cache-scope>public</cache-scope>
   		 ...
  	</portlet>
  

応答プロパティーまたは新規 CacheControl インターフェースを使って、ポートレットがプログラムによってキャッシュ・スコープを設定するようにもできます。しかし、キャッシュ・スコープをプログラムによって変更することはお勧めできません。なぜなら、ポートレットが同じユーザーに対してキャッシュ・スコープが共通に設定された応答と専用に設定された応答を組み合わせる場合、キャッシュ・エントリーが正しく無効化されているかどうかをポートレットが確認しにくくなるためです。

JSR 168 仕様では、ポートレット・ウィンドウとユーザーごとに単一のキャッシュ・エントリーのみが存在するように、ポートレット (レンダリングまたはアクション URL) に対するユーザー操作のたびにポートレット・コンテナーがキャッシュを無効化することを規定していました。共通レンダリング・パラメーターと共有キャッシュが導入されたことから仕様は拡張され、コンテナーがレンダリング・パラメーターに応じて複数のポートレット・ビューをキャッシュできるようになっています。この機能拡張が持つ意味は、レンダリング・パラメーターを使用すれば、共有キャッシュが可能なポートレット内でユーザーが異なるビュー間をナビゲートしても、キャッシュした出力をそのまま維持できるということです。現在、キャッシュの無効化が必要なのは、影響の程度がわからない副次作用をコンテナーにもたらす可能性のあるアクションおよびイベント処理の呼び出しのみとなっています。

妥当性検査に基づくキャッシュは、ポートレットのマークアップを計算するのはコストの高い演算となるため何度も繰り返したくないという場合に便利です。理想的には、有効期限の値を長めに設定して、マークアップが長期間キャッシュに入れられるようにしたほうがよい一方、バックエンド・システムでの変更に素早く応答して、ポートレットの更新済みビューを表示しなければならない場合もあります。このジレンマを解決するのが、妥当性検査に基づくキャッシュです。このキャッシュ機能では、ポートレットが頻繁に呼び出されるように短い有効期限を定義し、バックエンド・システムでの変更をチェックすることができます。バックエンド・システムで何も変更されていない場合、有効期限の切れたキャッシュの中身がまだ使用できるとして、CacheControl の setUseCachedContent(true) を設定して新しい有効期限を指定することにより、このキャッシュの内容を引き続き使用することができます。バックエンド・システムで何かが変更されている場合には、新しいマークアップを作成してこの設定を false に指定します。

現在キャッシュされているマークアップがどのバックエンド状態にマッピングされているかを知るには、ETag (HTTP 仕様での同じタグにちなんで付けられた名前) という特定の妥当性検査トークンを応答に設定します。すると、ポートレット・コンテナーは、次のレンダリングまたは serveResource 呼び出しの要求にこの ETag を指定することで、キャッシュされたコンテンツがまだ有効であることを示し、それによって妥当性検査が再度行えるようになります。

リスト 10 は、妥当性検査に基づくキャッシュの使用例です。


リスト 10. 妥当性検査に基づくキャッシュの例
                
protected void doView (RenderRequest request, RenderResponse response)
    throws PortletException, java.io.IOException
{
       if ( request.getETag() != null ) {  // validation request
	if ( markupIsStillValid(request) { 
		// markup is still valid
   		response.getCacheControl().setExpirationTime(30);
		response.getCacheControl().setUseCachedContent(true);
		// no need to write any output
		return;
	}
   }
   // create new content with new validation tag
   response.getCacheControl().setETag(computeETag(request));
   response.getCacheControl().setExpirationTime(60);
   PortletRequestDispatcher rd = 
   getPortletContext().getPortletRequestDispatcher("jsp/view.jsp");
   rd.include(request, response);
}

private boolean markupIsStillValid(PortletRequest request) {
    // check if backend state still matches validation token
    return computeETag(request).equals(request.getETag())
}

private String computeETag(PortletRequest request) {
    // return some backend state indicator like a last update timestamp
}

妥当性検査に基づくキャッシュを使用するとメリットがあるのは、バックエンド状態をチェックする操作に比べ、マークアップを作成する操作のコストが高い場合のみです。例えば、バックエンド・サーバーに対する要求がポートレットをレンダリングする時間の 90 パーセントを占めるのであれば、この妥当性検査に基づくキャッシュ機能を使用しても残りの 10 パーセントのレンダリング時間を節約するだけなので意味がありません。

ランタイム ID

ランタイム ID を使用してポートレットが処理するデータのスコープを設定すると、同じポータル内、あるいは同じページ上でのポートレットの複数オカレンスの衝突を防ぐことができます。ポートレット・セッションやポートレット・プリファレンスをはじめ、大抵のポートレット API オブジェクトは暗黙的に名前空間を設定しますが、共有データ・ストアにアクセスしたり、出力要素に固有の ID を割り当てたりする必要がある場合には明示的な名前空間の設定が必要です。このプラクティスは Ajax アプリケーションに共通しているもので、Java Portlet Specification のバージョン 2.0 がこの分野で提供する 2 つの強化された機能が特に役に立ちます。

その 1 つ目として、JSR 286 では、response.getNamespace メソッドによる名前空間ランタイム ID の存続期間が延長されました。名前空間ランタイム ID は今まで 1 つの要求に対してのみ有効でしたが、現在はポートレット・ウィンドウの存続期間中、この ID は変わりません。この延長により、例えばポートレットがフォーム・ベースのポータル・サーバーに集約されるように意図されている場合には、名前空間を使用して名前空間のフォーム ID を作成することもできるようになりました。さらに、Ajax レスポンスに組み込まれた JavaScript 関数呼び出しに名前空間を利用することや、前のレンダリング呼び出しによって提供された (名前空間を持つ) JavaScipt 関数を再利用することもできます。

2 つ目は、JSR 286 では新しい API 呼び出しが導入されています。この API 呼び出しでは、request.getWindowID メソッドによってポートレット・ウィンドウの固有 ID を取得することができます。取得した ID は、例えばポートレットがポートレット・ウィンドウごとにバックエンド・システムから受け取ったデータをキャッシュする必要がある場合などに、ポートレット・ウィンドウ単位で名前空間を作成する必要があるデータのキーとして使用することができます。ただし注意する点として、ポートレット・ウィンドウに関して定義されたライフサイクル呼び出しはないため、永続データ・ストアに名前空間エントリーを指定するためにポートレット・ウィンドウ ID を使用した場合には、これらのエントリーを自分でクリーンアップしなければなりません。

タグ・ライブラリーの追加

JSR 286 のタグ・ライブラリーには固有の名前空間があるため、新規タグ・ライブラリーの追加によって古い JSR 168 タグ・ライブラリーを使用するポートレットが干渉されることはありません。以下のコードを使って、新しいタグ・ライブラリーを組み込んでください。

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

オブジェクト定義のタグは拡張され、V1.0 での request、response、portletConfig の他に以下の変数も使用できるようになりました。

  • portletSession。ポートレット・スコープのセッションにアクセスするための変数です。
  • portletSessionScope。ポートレット・スコープのセッション属性のキー/値のマップにアクセスするための変数です。
  • portletPreferences。ポートレット・プリファレンスにアクセスするための変数です。
  • portletPreferencesValues。ポートレット・プリファレンスのキー/値のマップにアクセスするための変数です。

URL 生成タグのそれぞれに、以下の 2 つの追加属性があります。

  • copyCurrentRenderParameters。現行の専用レンダリング・パラメーターを URL にコピーします。
  • escapeXml。URL の XML エスケープに関するデフォルト設定のオン/オフを切り替えます。前にも説明したように、JSR 168 では URL が XML にエスケープされるかどうかは定義されていませんでした。JSR 286 では JSTL (Java Standard Tag Library) と同じ振る舞いを定義しており、デフォルトですべての URL はエスケープされますが、この escapeXml 属性を使用することでその設定を無効にすることができます。

アクション URL についても、GenericPortlet が ProcessAction アノテーション付きメソッドにディスパッチするために評価する javax.portlet.action パラメーターの設定を可能にする追加の name 属性があります。

JSR 286 仕様には、この他にも以下の追加が行われています。

  • リソース URL を生成するための新規 resourceURL タグの追加
  • プロパティーを URL にアタッチするためにポートレット URL タグ内部で使用できる新規 propertyTag の追加

まとめ

この記事で説明したように、Java Portlet Specification のバージョン 2.0 には新しい内容と機能が盛りだくさんに追加されています。仕様の内容と API の数はバージョン 1.0 の 2 倍以上です。このような成長を果たした Java Portlet Specification では、ベンダーの拡張機能を使用せずにほとんどの使用事例を実装することができます。この記事で説明した機能の一部はオプションなので、JSR 286 準拠のすべてのプラットフォームでサポートされるとは限りませんが、この標準が、拡張機能を提供するプラットフォームについては一貫した明確な方法で機能をサポートすることを確実にします。

また、ポートレットのプログラミング・モデルにはイベントと共通レンダリング・パラメーターが用意されているため、現在お使いのポートレットから大規模な複合アプリケーションを構築し、さまざまなシナリオでそのポートレットを再利用することができます。さらに、リソースを直接ポートレットから供給できるようになった今、JSR 286 では Ajax ベースの使用事例のサポートも改善されています。


参考文献

著者について

Stefan Hepper

Stefan Hepper は、WebSphere Portal および Workplace のプログラミング・モデルと公開 API を担当するアーキテクトです。Java Portlet Specification V1.0 (JSR 168) のリーダーの 1 人であった彼は、現在 V2.0 (JSR 286) への取り組みでリーダーを務めています。Stefan はドイツの University of Karlsruh でコンピューター・サイエンスの学位を取得し、1998年に IBM Böblingen Development Laboratory に入社しました。

Oliver Koeth photo

Oliver Köth は、WebSphere Portal のポートレット・コンテナーの開発リーダーで、この製品での Java Portlet Specification 2.0 実装を担当しています。ドイツの University of Erlangen でコンピューター・サイエンスの学位を取得し、2001年から IBM Böblingen Development Laboratory で IBM のポータル・ミッションに取り組んでいます。

不正使用の報告のヘルプ

不正使用の報告

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


不正使用の報告のヘルプ

不正使用の報告

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


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=WebSphere, Java technology
ArticleID=302249
ArticleTitle=Java Portlet Specification V2.0 (JSR 286) の新たな内容とは?
publish-date=03182008
author1-email=sthepper@de.ibm.com
author1-email-cc=Lotus Support Web Site Teamroom <NDL> <REPLICA 85256F2E:0046D56B> <HINT>CN=Iris2/O=NotesWeb</HINT> <REM>Lotus Support Web Site Teamroom</REM> debcot@us.ibm.com
author2-email=okoeth@de.ibm.com
author2-email-cc=

タグ

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

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

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

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

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