Apache Pluto の覚書きです。Apache Pluto は、Java Portlet 仕様 のリファレンス実装です。この覚書きを書いている段階では、Java Portlet Specification (JSR-168) が Java Portlet 仕様として、まとめられています。今回は、Java Portlet Specification を読んだ覚書きを記録していこうと思います。その後で、Apache Pluto を触った覚書を記録していこうと思います。
この覚書きで使っている環境は次の通りです。
- Eclipse 3.1.2
- Java Portlet Specification Version 1.0
- Pluto 1.1 (alpha release 前のもの)
- Tomcat 5.5.9
- J2SE 5.0
参考にしたリソースは次の通りです。
- Java Portlet Specification Version1.0 (JSR-168)
- Developing Portlets with Apache Pluto
- ONJava.com - What Is a Portlet, Part 2
間違い等に気づいた方は、ご連絡いただけると助かります。(英語が苦手なもので –;)
Java Portlet Specification (JSR-168)
目次
- 言葉の定義(What is ○○?)
- Servlet との違いは?
- ポータルページの構成
- ポータルのリクエスト処理手順
- Portlet API
- Dispatching Request to Servlet and JSPs
- ユーザ情報
- セキュリティ
- portlet.xmlの例
言葉の定義(What is ○○?)
ポータルとは何か?
ポータルとは、Personalization(個別化)、Single Sign on(シングルサインオン)、Content aggregation(コンテンツの統合)を行うプレゼンテーション層の Webアプリケーションです。
Portlet とは何か?
Portlet とは、ポータルのための Java 技術の一つであり、Portlet コンテナによって管理される、ユーザインターフェースコンポーネントです。
Portlet コンテナとは何か?
Portlet コンテナとは、Portlet を管理する実行時環境のことです。Portlet のライフサイクルを管理します。永続化の仕組みや、Portlet の初期化、ポータルページからのリクエストを処理します。
fragment とは何か?
Portlet によって生成されるコンテンツは、fragment とよばれます。fragment とは、HTML や XHTML の破片のことです。つまり、完全な HTML ページではなく、例えば一つの div タグからなる要素だったりします。
fragment は、ポータルページに埋め込まれたときに完全なコンテンツになるように、ルールに則って作る必要があります。。このルールは、次のように決められています。
- Warning
-
Portlet が生成する HTML fragment は、次のタグを含んではいけません: base、body、iframe、frame、frameset、head、html、title
Portlet が生成する XHTML や XHTML-Basic fragment は、次のタグを含んではいけません: base、body、iframe、head、html、title
Portlet が生成する fragment がポータルページとしてなじむために、次のタグの使用に注意しなければなりません:link、meta、style
Servlet との違いは?
Portlet は、Servlet と次のような違いがあります。
- Portlet の生成するコンテンツは、fragment なので、それ自体では完結したコンテンツにならない
- そのため、Portlet と URL とは、直接的な関係を持たない
- Web クライアントは、Portlet 単体ではなく、ポータルページとやり取りをする
- 同時に複数の Portlet がポータルページに存在する
- Portlet は、定義されたライフサイクルを持つ
- Portlet は、Window State という定義された状態をもつ
Portlet は Servlet には無い、次の機能をもちます。
- 設定情報にアクセスする機能を持つ
- Portlet はユーザプロファイルにアクセスできる
- Portlet 用のURLを作成する機能を持つ
- 2つの Session スコープをもつ(詳細後述)
Portlet は、Servlet の次の機能が使えません。
- レスポンスの文字エンコードを指定できない
- HTML レスポンスヘッダを設定できない
- ポータルページへしかページリクエストができない(つまり、Portlet を直接呼び出せない)
同一Portlet アプリケーション内にある Portlet、Servlet、JSP は、ClassLoader、アプリケーションコンテキスト、セッションを共有します。
Portlet のライフサイクル
Portlet は次のライフサイクルを持ちます。これらのライフサイクルにあわせて、javax.portlet.Portlet インターフェースに定義された同名のメソッドが呼び出されます
- init
- processAction
- render
- destroy
ライフサイクルは、次のように処理されていきます。
- Portlet コンテナが起動したとき、もしくは最初に Portlet へのリクエストが発生したときに、Portlet の init メソッドが呼び出されます
- 後述する ActionURL がリクエストされたときに、processAction メソッドが呼び出されます
- processAction の処理が終了した後、もしくは RenderURL がリクエストされたときに、render メソッドが呼び出されます
- Portlet インスタンスが不要になったときに、destroy メソッドが呼び出されます
init メソッドは、Portlet インスタンスが初期化される最初の一回だけ呼び出されます。destory メソッドが呼び出されたあとの Portlet インスタンスは、ガーベッジコレクションの対象になります。
Portlet Mode
Portlet の動作モードのことです。下記の3つが決められています。Portletは VIEW モードを必ずサポートしなければなりません。これ以外のモードは、必要に応じてサポートします。カスタムモードもつくることができます。
- VIEW
- VIEWモードは、コンテンツを静的に画面に表示するモードのことで、一般の閲覧行為にはこのモードが使われます。
- EDIT
- EDITモードは、編集系のページに用意されるモードで、CMSなどのPortletは、このモードをサポートします。
- HELP
- HELPモードは、Portlet の説明を表示するモードです。
Window State
ポータルページ上のPortletコンテンツは、下記のいずれかの状態を持ちます。
- NORMAL
- NORMAL 状態はPortletが他のコンテンツと共存している状態です。ポータルページに複数の Portlet が表示されることになります。
- MAXIMIZED
- MAXIMIZED 状態は、自分だけがページに表示されている状態です。ポータルページには、自分の Portlet しか表示されません。
- MINIMIZED
- MINIMIZED 状態は、自分以外だけがページに表示されている状態です。ポータルページには、自分の Portlet は表示されません。この状態のときは、Portlet はなにもレンダリングするべきではありません。
ポータルページの構成
ポータルページ は、複数の Portlet Application で構成されます。Portlet Application は、コントロールを持ちます。Windows State を変更することができます。Portlet Application が生成するコンテンツが Portlet fragmentです。
ポータルのリクエスト処理手順
ポータルページは、次のように処理されていきます。ポータルの動作は、次の4つのプロセスに分かれます。
- Request Handling
- Action Request
- Render Request
- End of Service
Request Handling
Portlet コンテナは、クライアントからのリクエストを受けると、トリガーとなったPortletURL の種類を判別します。 ActionURL の場合は、Portlet の processAction メソッドが呼び出されます。それ以外の場合は、Portlet の render メソッドが呼び出されます。
Action Request
Portlet#processAction メソッドが呼び出されます。Portlet に対するリクエストを処理します。PortletURL の種類が RenderURL ではない場合にこのプロセスが処理されます。リクエストに対して、Portlet の状態を変化させたり、ビジネスロジックを実行したりします。Struts のActionのイメージで利用すると良いと思います。ただし、Portlet は、 アクションとレンダリングを区別しているので、processAction ないでは、処理のみを行うようにします。
processAction の引数には、ActionRequest と ActionResponse オブジェクトが渡されます。これは、ServletRequest と ServletResponse とほとんど同じものと考えればよさそうです。ActionRequest には、クライアントから送信されたリクエストパラメータが含まれています。
- リクエストパラメータのライフサイクル
- リクエストパラメータは、processAction メソッドを抜けると、破棄されてしまいます。つまり、render メソッド内で取得することができません。ただし、RenderURL によってリクエストが呼び出されて、processAction が呼ばれなかった場合は、render メソッド内で、リクエストパラメータを取得することができます。
processAction 内で生成したパラメータで、Render Request に必要なパラメータは、ActionResponse#setRenderParameter メソッドを使って render メソッドまで保持しておくことができます。setRenderParameter メソッドで設定された値は、RenderRequest#getParameter メソッドで取得することができます。これにより、processAction メソッド内で設定したパラメータを render メソッドで取得することが出来ます。なお、setRenderParameter でセットする key と value は、null を許可しません。null の場合は例外がスローされます。
Render Request
このプロセスでは、Portlet は、自身の Window State にあわせたコンテンツを出力します。Portlet の render メソッドが呼び出されます。render メソッドの引数の RenderRequest オブジェクトを使って、Portlet の様々な情報にアクセスすることができます。ServletRequest と同じような使い方ができます。
コンテンツを書き出すには、render メソッドの引数に渡される RenderResponse オブジェクトを使います。直接ストリームにコンテンツを書き出す方法や、Servlet や JSP に処理をディスパッチすることもできます。ServletResponse オブジェクトと同じような使い方ができます。
End of Service
Portlet コンテナは、Portlet を長い期間ロードしたままにしておかなければならないと言うことは規定されていません。つまり、一度の処理で Portlet は破棄されるかもしれないし、されないかもしれないと言うことです。Portlet が破棄されるときには destroy メソッドが呼び出されます。
Portlet API
javax.portlet.Portlet
Portlet クラスが実装するインターフェースです。
javax.portlet.GenericPortlet
Portletのデフォルト実装です。通常、このクラスを継承して、Portletクラスを作成します。このクラスには、doView、doEdit、doHelp メソッドが用意されています。これらのメソッドは、VIEW モードのときは、doView メソッドという風に、Portal Mode によってレンダリング時に呼び出されます。
javax.portlet.PortletURL
Portletは、PortletURLオブジェクトを使ってPortlet のURLを表します。PortletURL オブジェクトは、javax.portlet.RenderResponse によって生成されます。PortletURLは、つぎの2つの種類があります。
-
Action URL
Portlet#processAction のトリガーになるURLです。 -
Render URL
Portlet#render のトリガーになるURLです。このURLが呼び出されたときには、Portlet の processAction は呼び出されません。
javax.portlet.PortletContext
Portlet 版 ServletContext です。Portlet コンテナにアクセスする方法を提供します。ここに格納された情報は、同一ポートレットアプリケーション(同一のwarファイルに含まれているPortlet群)のすべてのユーザ、すべてのPortlet で共有されます。PortletContext はポートレットアプリケーションにつき、一つ用意されます。
PortletContext は、Portlet の初期パラメータにアクセスすることができます。また、ServletContext とパラメータを共有することもできます。web.xml に記述した初期化パラメータ等にも、PortletContext からアクセスできます。log メソッドを呼び出すことで Portlet ログファイルにログを書き出すことができます。
PortletContext の情報は、ServletContext の情報と共有されます。つまり、PortletContext に格納した情報は、ServletContext で取得できるし、ServletContext に格納した情報は、PortletContext で取得できます。
javax.portlet.PortletRequest
Portlet 版 HttpServletRequest です。クライアントのリクエストに含まれるパラメータを取得したり、セッションを開始したりすることができます。Portlet コンテナは、javax.portlet.RenderRequest を用意しています。これらは、processAction メソッド、render メソッドのそれぞれで使われます。PortletRequest、PortletResponse は、スレッドセーフではありません。
javax.portlet.ActionRequest
Portlet の processAction メソッドの引数に渡されるリクエストオブジェクトです。PortletRequest を継承していて、アクションリクエスト(ActionURL によるリクエスト)を処理するのに使います。ActionURL にセットされたパラメータを取得することができます。
javax.portlet.RenderRequest
Portlet の render メソッドの引数に渡されるリクエストオブジェクトです。 PortletRequest を継承していて、レンダリングリクエスト(RenderURL によるリクエスト)を処理するのに使います。RenderURL にセットされたパラメータを取得することができます。
javax.portlet.PortletResponse
Portlet 版 HttpServletResponse です。Portlet コンテナは、javax.portlet.RenderResponse の2つを用意しています。これらは、processAction メソッド、render メソッドのそれぞれで使われます。
javax.portlet.ActionResponse
processAction メソッドのレスポンスを書き込むオブジェクトです。render メソッドにパラメータを渡すには、 setRenderParameter メソッドを利用します。また、レスポンスをリダイレクトする場合には、sendRedirect メソッドを呼び出します。
javax.portlet.RenderResponse
render メソッドのレスポンスを書き込むオブジェクトです。コンテンツの書き出しや、PortletURL の生成を行います。
javax.portlet.PortletPreferences
Portlet のプリファレンスにアクセスするためのクラスです。PortletRequest#getPreferences メソッドを呼び出すことで取得できます。プリファレンスは、 /WEB-INF/portlet.xml ファイルに記述します。Portlet 毎に設定します。
プリファレンスの情報は、processAction メソッド中であれば、変更可能です。render メソッドで変更すると、例外がスローされます。
javax.portlet.PortletSession
Portlet 版 HttpSession です。PortletSession のスコープには2つあります。
- Application Scope
- Portlet Scope
Application Scope は、同一ポートレットアプリケーション内で共有されるスコープです。Portlet Scope は、同一Portlet で共有されるスコープです。Portlet Scope の方が狭いスコープになります。プライベートスコープとも呼ばれるみたいです。スコープを指定するには、引数を3つとる setAttribute メソッドを呼び出します。
Portlet Scope に格納されるオブジェクトのキーは、Portlet コンテナによって、次のように決められるそうです。
javax.portlet.p.<ID>?<ATTRIBUTE_NAME>
<ID> は、Portlet で一意に決められる値で、’?‘ は含まれません。<ATTRIBUTE_NAME> は、setAttribute メソッドの引数に渡した名前です。
PortletSession に格納された情報は、HttpSession と共有されます。つまり、PortletSession に格納した値は、HttpSession で取得でき、HttpSession で格納した値は、PortletSession で取得できます。セッションキーに付ける名前は、パッケージ名を参考に付けるとよいようです。ただし、javax.portlet というプレフィックスは予約されているので、開発者は使わないようにしましょう。
HttpSession に保存された情報は、PortletSession の APPLICATION_SCOPE になります。逆もしかりです。
Dispatching Request to Servlet and JSPs
ディスパッチの方法
Portlet#render メソッド中で、PortletRequestDispatcher を使ってリクエストをディスパッチすることができます。PortletRequestDispatcher は、PortletContext オブジェクトから取得することができます。ディスパッチするパスは、’/‘ で始まる必要があります。PortletContext のルートパスからの相対パスになります。パスには、クエリストリングを付けることが出来ます。getNamedDispatcher メソッドを使うと、サーブレット名を指定して、ディスパッチャを取得することが出来ます。
PortletRequestDispacher によってディスパッチされたサーブレットや JSP には、HTTP GET がリクエストされます。
- forward してはいけない場合
PortletRequestDispacher によって include された Servlet や JSP からは、 RequestDispatcher#forward メソッドを呼び出してはいけません。代わりに RequestDispatcher#include メソッドを呼び出すか、Servlet 内ではアクション処理だけを行い、Portlet の render メソッドで別の Servlet や JSP を include します。
ディスパッチ時に付与されるパラメータとアトリビュート
getRequestDispatcher メソッドを使って取得したディスパッチャ(getNamedDispatcher以外の方法のこと) を使ってサーブレットやJSPにリクエストをディスパッチした時、HttpServletRequest のアトリビュートに下記の属性がセットされます。
- javax.servlet.include.request_uri
- サーブレットのリクエストURI。例:/PortalDemo/index.do
- javax.servlet.include.context_path
- サーブレットのコンテキストパス。例:/PortalDemo
- javax.servlet.include.servlet_path
- サーブレットパス。例:/index.do
- javax.servlet.include.path_info
- パス情報。
- javax.servlet.include.query_string
- クエリストリングの情報。例:param1=PARAM1¶m2=PARAM2
ディスパッチの方法にかかわらず、Portlet からサーブレットやJSPにリクエストをディスパッチしたときには、HttpServletRequest に下記の属性がセットされます。
- javax.portlet.config
- javax.portlet.PortletConfig クラスのオブジェクト
- javax.portlet.request
- javax.portlet.RenderRequest クラスのオブジェクト
- javax.portlet.response
- javax.portlet.RenderResponse クラスのオブジェクト
その他、Portlet で設定した Parameter や Attribute が、サーブレットやJSPから取得できます。
ディスパッチされたサーブレットの制限事項
PortletRequestDispatcher を使ってリクエストをディスパッチされたサーブレットには、かなりの制限がほどこされます。
HttpServletRequest
メソッド名 | 制約 |
---|---|
| null を返す |
| PortletRequestDispatcher の情報を返す |
| PortletRequest の同名のメソッドと同じ処理になる |
| PortletRequest の同名のメソッドと同じ処理になる。Java Portlet Specification version1.0 PLT.16.1.1 Query Strings in Request Dispatcher Paths セクションに書かれている定義の通りに動作する |
| 何もしない か nullを返す |
| 常に 0 を返す |
|
PortletRequest の getProperties メソッドによって提供される情報を返す |
| 常に GET を返す |
HttpServletResponse
メソッド名 | 制約 |
---|---|
| null を返す |
| RenderResponse の同名のメソッドと同じ処理になる |
| 何もしない |
| 常に false を返す |
| RenderResponse の同名のメソッドと同じ処理になる |
ユーザ情報
TBD
セキュリティ
Portlet コンテナは、ユーザの権限をチェックして、Portlet にアクセス可能かどうかを確認します。これには、Servlet 仕様で決められている、Role を使います。開発者がプログラマティックにチェックすることもできます。それには、PortletRequest クラスの次のメソッドを使用します。
- getRemoteUser
- 認証に使われたユーザ名を返します。
- isUserInRole
- 指定したロール名が、セキュリティロールで許可されているかどうかを確認します。
- getUserPrincipal
- ログインユーザの Principal を返します。