Apache Pluto の覚書きです。Apache Pluto は、Java Portlet 仕様 のリファレンス実装です。この覚書きを書いている段階では、Java Portlet Specification (JSR-168) が Java Portlet 仕様として、まとめられています。
この覚書きで使っている環境は次の通りです。
- Eclipse 3.1.2
- Pluto 1.1 (alpha release 以前)
- Tomcat 5.5.9
- J2SE 5.0
参考にしたリソースは次の通りです。
Pluto の中を追っていったときの覚書です。ソースコードを手元に読んでいただければ、より理解できるのではないかと思います。
間違い等に気づいた方は、ご連絡いただけると助かります。
Apache Pluto1.1 覚書 目次
Pluto をもうちょっと詳しく見てみる 目次
環境構築
今回使うのは、Apache Pluto1.1 です。2006/4/20 現在では、 Pluto のバージョンは 1.0.1 と 1.1 の二つが用意されています。バイナリで配布されているのは 1.0.1 だけなので、今回はソースコードを手に入れるところからはじめます。
Pluto 1.0.1 と Pluto 1.1 は、アーキテクチャが大きく変わったようです。1.1 のほうがよりシンプルに美しくなりました。それに伴って、設定の仕方なども色々と変わっています。 Pluto 1.0.1 を使う場合は、この覚書は参考にならないかもしれませんので注意してください。
今回使うのは、Pluto1.1 です。
ライセンス
The Apache Software License, Version 2.0
ソースコードをダウンロードする
Apache Pluto 1.1 のソースコードは、Subversion で管理されています。下記の URL からチェックアウトできます。
http://svn.apache.org/repos/asf/portals/pluto/trunk/
下のコマンドを実行することでも、ソースコードがダウンロードできます。
> svn checkout http://svn.apache.org/repos/asf/portals/pluto/trunk/ pluto-site
インストールする
ソースコードをダウンロードしたら、Apache Pluto のサイトにある Getting Started を見ながら、インストールを行います。ソースコードをダウンロードしたパスに移動して
> mvn install
を実行すると、ずらずらと、ライブラリができるので、Getting Started に書いてある Step にしたがって配置していきます。
配置が終了したら、Tomcat を起動します。http://localhost:8080/pluto/portal にアクセスすると、Pluto のポータル画面が表示されます。
Pluto のアーキテクチャ
ポータルもWebアプリケーションだから、サーブレット が使われている
まず、最初に Pluto がどういう風に動作するのかを簡単にまとめて見ました。(画像をクリックすると大きな画像が表示されます。)
Pluto は、Web ブラウザからリクエストを受けると次のように動作します。
1. PortalDriverServlet がポータルに対するリクエストを受け取る ↓ 2. サーブレットコンテキスト、サーブレットリクエスト、サーブレットレスポンスをそれぞれポートレット API でラップする ↓ 3. ポートレットコンテナを呼び出す ↓ 4. ポートレットコンテナは、リクエストパラメータを解析して、どのポートレットが呼び出されたのかを判断する ↓ 5. 呼び出されたポートレットにリクエストをフォワードする ↓ 6. ポートレットコンテナは、レンダリングリクエストを表示するページ内の全ポートレットに対して送信する ↓ 7. ポートレットのレスポンスをマージして、画面をブラウザに返す
ポータルアプリケーションもひとつの Web アプリケーションなので、当然サーブレットを使っています。
Tomcat を起動した際に webapp ディレクトリにできる pluto フォルダの下には、典型的な Web アプリケーションの構成ができています。
{TOMCAT_HOME}/webapps/pluto/WEB-INF/web.xml を開くと、こんな記述があると思います。
web.xml 抜粋
<servlet>
<servlet-name>plutoPortalDriver</servlet-name>
<display-name>Pluto Portal Driver</display-name>
<description>Pluto Portal Driver Controller</description>
<servlet-class>org.apache.pluto.driver.PortalDriverServlet</servlet-class>
</servlet>
このクラスが、ポータルアプリケーションの窓口、ポータルアプリケーションのサーブレットです。ポータルにアクセスしたいときは、このサーブレットにアクセスすることになります。web.xml の下のほうにマッピングが書かれていますので、見てみます。
web.xml 抜粋
<servlet-mapping> <servlet-name>plutoPortalDriver</servlet-name> <url-pattern>/portal/*</url-pattern> </servlet-mapping>
つまり、http://localhost:8080/pluto/portal/ にアクセスすることで、このサーブレットが呼ばれ、ポートレットコンテナに格納されているポートレットが呼び出されるわけです。
なお、ポートレットの初期化は、サーブレットリスナで行われており、org.apache.pluto.driver.PortalStartupListener
が使われていました。これについては後の節で詳しく見ることにします。
Pluto で使われる主なクラス群
Pluto のアーキテクチャを調べた中で出てきた主なクラスは次の4つです。
- org.apache.pluto.driver.PortletDriverServelt
このクラスは、Pluto の本体ともいえるサーブレットの実装クラスです。Web ブラウザからのアクセスは、このサーブレットが受け取ります。このクラスは、Pluto 全体の初期化やポートレットコンテナの作成と初期化を行います。
- org.apache.pluto.PortletContainer
ポートレットコンテナの実装クラスです。PortletDriverServlet から呼び出されます。内部にはポートレットを格納しています。リクエストを解釈し、適切なポートレットに割り振ることを行います。
- org.apache.pluto.core.PortletServlet
ポートレットアプリケーション側に配備されるクラスです。ポートレットコンテナからの呼び出しを受け、ポートレットに処理を委譲します。
- org.apache.pluto.driver.tags.PortletRenderTag
ポートレットのレンダリングを呼び出すためのタグクラスです。JSP に記述され、ポートレットのレンダリングリクエストを発行します。
<pluto:render />
であらわされます。
- クロスコンテキストを有効にする必要がある
- ポータルは、複数のWeb アプリケーションをまたがるため、クロスコンテキスト(crossContext) が可能でなければなりません。{TOMCAT_HOME}/conf/Catalina/localhost にコピーした pluto.xml には、
crossContext="true"
が記述されています。
ポートレットコンテナにポートレットを登録するにはどうすればいいのか?
{TOMCAT_HOME}/webapps/pluto/WEB-INF/web.xml を見ると、いくつかのサーブレットの設定に org.apache.pluto.core.PortletServlet
というクラスが使われています。サーブレットの init-param
設定で、portlet-name
というパラメータが設定されています。
<servlet>
<servlet-name>AdminPortlet</servlet-name>
<servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-name</param-name>
<param-value>AdminPortlet</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
どうやら、Pluto1.1 では、ポートレットコンテナ がこの PotletServlet
を呼び出して、さらに PortletServlet
が ポータルコンテンツ を呼び出す仕組みになっているようです。呼び出される ポータルコンテンツは、portlet-name
で指定しています。
portlet-name
で指定されているのは、あくまでポートレット名です。では、ポートレットの本体はどこで定義するのかというと、JSR-168 で決められている通り、portlet.xml に定義します。あたりまえですね。
web.xml をもう少し見ていくと、今度は、servlet-mapping
が見つかります。
<servlet-mapping>
<servlet-name>AdminPortlet</servlet-name>
<url-pattern>/PlutoInvoker/AdminPortlet</url-pattern>
</servlet-mapping>
/PlutoInvoker/
という記述がなんとも不思議です。Pluto 1.1 は、PortletServlet
が呼び出される URL にプレフィックスとして、この /PlutoInvoker/
という名前を使います。
Portlet 用のサーブレットは、マッピング URL に /PlutoInvoker/
を記述するようにします。その後ろの名前は、ポートレット名をつけます。
ちなみに、web.xml と portal.xml の設定は、ポートレットアプリケーション(自分が作るアプリケーション)側に記述します。
ポートレットを呼び出すぞ。(でも、URL がわからない・・・)
さて、これでポートレットの設定は完了したわけですが、これだけではポートレットは呼び出せません。JSR-168 には、
Portlets are not directly bound to a URL
とあります。これはつまり、ブラウザのアドレスバーに URL を直接打ち込んでもポートレットを呼び出すことはできませんということを言っています。例えば、ブラウザのアドレスに http://localhost:8080/pluto/PortletInvoker/AdminPortal 入力してもエラーになります。
では、どうすればよいかというと、ポートレットは、ポートレットコンテナから呼び出されることが決められているので、ポートレットコンテナに呼び出しをお願いします。Pluto1.1 の場合、このお願いは {TOMCAT_HOME}/webapps/pluto/WEB-INF/pluto-portal-driver-config.xml に記述することになります。
(pluto-portal-driver-config.xml 抜粋)
<pluto-portal-driver
...
<portlet-app>
<context-path>/pluto</context-path>
<portlets>
<portlet name="AboutPortlet" />
<portlet name="AdminPortlet" />
</portlets>
</portlet-app>
...
<render-config default="Pluto Admin">
<page name="Pluto Admin" uri="/WEB-INF/themes/pluto-default-theme.jsp">
<portlet context="/pluto" name="AdminPortlet"/>
</page>
</render-config>
</pluto-portal-driver>
portlet-app タグ
このタグでポートレットの定義を記述します。
- context-path タグ
ここに、ポートレットのコンテキストパスを記述します。ポートレットは別の Web アプリケーションとして作成できるので、コンテキストも Pluto とは別になります。コンテキスト名に使える文字は 「a-z, A-Z, 0-9, _, /」 です。’.’ や ‘-’ は使えないので注意です。
- portlets タグ
このタグを使って、このコンテキストに含まれるポートレットの名前を定義します。ここに記述するのは、portlet.xml に定義したポートレットです。
render-config タグ
このタグは、PlutoPortletDriver
の設定を記述するところです。ここには、レンダリングに関する設定をおこないます。設定しているのは、ポータルページを呼び出すときのパス名(name 属性)と、ポータルページに使用するページ(uri 属性)、どのポートレットを使うか(portlet タグ)です。portlet タグに記述するのは、portlet-app
で記述したコンテキストと、ポートレット名です。
name に指定した値 (ページ名) をブラウザのアドレスバーに打ち込むことで、ポータルページを開くことができます。
http://localhost:8080/pluto/portal/Pluto%20Admin
これで、Pluto を起動したときに、pluto-portal-driver-config.xml が読み込まれ、ポートレット URLを生成したページが表示できるようになります。Pluto は、この設定ファイルを解析して、URL と ポートレットアプリケーションのマッピングを行います。
http://localhost:8080/pluto/portal にアクセスして、ログインした後のページの左上にある Navigation から ページを移動してみましょう。画面が表示されるはずです。
- ポートレットID
-
すべてのポートレットには、一意の ID が振られます。Pluto の場合、次のようなルールによって ID が決められます。
[portlet-context-name].[portlet-name]
[portlet-context-name]
とは、ポートレットアプリケーションのコンテキスト名のことです。[portlet-name]
は、文字通りポートレット名です。これらを ‘.’ でつないだものが Portlet Definition ID と呼ばれるポートレットの一意な ID です。
Pluto の設定には、SpringFramework が使われている
{TOMCAT_HOME}/webapps/pluto/WEB-INF/ を見ると、pluto-portal-driver-service-config.xml というファイルがあるのがわかります。このファイル、中を見ると実は SpringFramework の Bean 設定ファイルだったりします。
Pluto の設定が行われている部分を抜粋します。
(pluto-portal-driver-service-config.xml 抜粋)
<bean id="DriverConfiguration"
class="org.apache.pluto.driver.config.impl.DriverConfigurationImpl">
<!-- ===== Portal Services ===== -->
<constructor-arg><ref local="PropertyConfigService"/></constructor-arg>
<constructor-arg><ref local="PortletRegistryConfig"/></constructor-arg>
<constructor-arg><ref local="RenderConfigService"/></constructor-arg>
<!-- === Container Services === -->
<constructor-arg><ref local="PortalCallbackService"/></constructor-arg>
<!-- Optional Container Services -->
<!--
<property name="portletPreferencesService" >
<ref local="PortletPreferencesService" />
</property>
<property name ="userAttributeService" >
<ref local="UserAttributeService" />
</property>
-->
</bean>
4つのサービスクラスをインジェクションしています。Portal Services とコメントされているサービスクラスは、pluto-portal-driver-service-config.xml を読み込んで、それぞれの担当範囲の設定を返す役割を持っています。
Container Services とコメントされているサービスクラスは、ポートレットコンテナから必要に応じて呼び出されるクラスです。例えば、ポータルのタイトルを設定するときに呼び出されたり、ポートレット URL を生成するクラスの取得に使われたりします。
Pluto の実装を拡張したものに、uPortal があります。これらのサービスクラスを拡張して作られています。
まとめ
- ポータルはただのWeb アプリケーション
- ポートレットもただのWeb アプリケーション
- ポータルアプリケーションとポートレットアプリケーションは別々に開発できる
- ポータルアプリケーションの設定は{PORTLET_APP}/WEB-INF/portlet.xml を作成することで行う
- ポートレットコンテナに対するポートレットの追加・変更や削除は {PLUTO_HOME}/WEB-INF/pluto-portal-driver-config.xml に記述する
- 画面デザインの変更は、{PLUTO_HOME}/WEB-INF/themes/pluto-default-theme.jsp、portlet-skin.jsp を変更することで行う
- ポートレットコンテナの初期化に関する設定は、{PLUTO_HOME}/WEB-INF/pluto-portal-driver-service-config.xml に記述する
さらに、ポートレットアプリケーションの開発者がやらなければならないのは、次の点です。
- Portlet 開発者がやる作業
- javax.portlet.GenericPortlet を継承したポートレットを作成する
- /WEB-INF/portlet.xml を作成し、ポートレットの定義を行う
- web.xml に org.apache.pluto.core.PortletServlet を設定し、
portal-name
を記述する - Pluto の pluto-portal-driver-config.xml に、ポートレットの設定を記述する
PortalStartupListener の動作
もうちょっと突っ込んで Pluto を見てみます。といっても、PortalDriverServlet
と PortletContainer
の動きを追っていくだけですが。
PortalStartupListener
の中で、Pluto で使われるいろいろな初期設定が行われます。このクラスは、Pluto がアプリケーションサーバにロードされると起動します。デフォルトの実装では、次の3つが行われます。
デフォルトの設定メソッド
- initDriverConfiguration(servletContext);
- initAdminConfiguration(servletContext);
- initContainer(servletContext);
順番に見ていきます。まずは、initDriverConfiguration
です。
このメソッドは、DriverConfiguration
の生成と初期化を行います。
最初に DriverConfigrationFactory
クラスが呼び出されます。 Factory の中で、SpringFramework が呼び出され、 pluto-portal-driver-service-config.xml の内容を元に DriverConfigration
が作成されます。
あとは、DriverConfiguration
に設定されたサービスクラスの init
メソッドが呼び出され、pluto-portal-driver-config.xml のパースを行うという仕組みです。
生成された DriverConfiguration
は、ServletContext
に "driverConfig"
という名前で格納されます。
つぎに、AdminConfiguration
の生成と初期化が行われます。
ほとんどの処理が、DriverConfiguration
の同じです。違うのは、ServletContext
に格納するときのキーが違うくらいです。
"driverAdminConfig" という値で格納されます。
AdminConfiguration
は、ポートレットの追加や削除がシステム的に行えるものです。ポートレットを管理するツールを作るときに使えそうです。
最後に ポートレットコンテナの生成と初期化です。
ここも大したことをしていません。DriverConfiguration
をもとに、コンテナの実装クラスを生成しているだけです。
生成したコンテナは、ServletContext
に "portletContainer"
というキーで格納されます。
これで Pluto の初期化が完了しました。あとは、PortletDriverServlet
と PortletContainer
の動作がわかれば、一通り Pluto の動作を追えるかなと思います。
PortletDriverServlet の動作
このクラスは、何をやっているかというと、サーブレット API を ポートレット API にラップしてポートレットコンテナを呼び出すということを行っています。
ポータルは、リクエストパラメータの値によって呼び出すポートレットを判断します。リクエストパラメータとポートレットのマッピングルールは、ポータル製品ごとに決められています。
Pluto 1.1 は、PortalURLParser
クラスの parse
メソッドで変換ルールを規定しています。ここで、リクエスト URL をポータルで使える形に変換しています。
リクエスト種類によって、呼び出しが変わる
ポータルに対するリクエストには種類があります。一つはアクションリクエストで、ポートレットの processAction
メソッドが呼び出されます。もう一つはレンダリングリクエストで、ポートレットの render
メソッドが呼び出されます。
アクションリクエストの場合、ポートレットコンテナの doAction
が呼び出されます。これは、ポートレットの呼び出しを行うメソッドになっています。
レンダリングリクエストの場合、画面の描画が行われます。このとき、{PLUTO_HOME}/WEB-INF/themes/pluto-default-theme.jsp の表示されます。
ポートレットコンテナの動作
ポートレットコンテナの呼び出しを整理しておくと
JSR-168 では、アクションURL がリクエストされたときには、対応するポートレットの processAction
メソッドの呼び出しと、ポータルページに含まれるすべてのポートレットの render
メソッドが呼び出されるとされています。
If the client request is triggered by an action URL, the portal/portlet-container must first trigger the action request by invoking the processAction method of the targeted portlet. <中略> Then, the portal/portlet-container must trigger the render request by invoking the render method for all the portlets in the portal page with the possible exception of portlets for which their content is being cached.
PortletDriverServlet
から呼び出されるのは、ポートレットコンテナの doAction
メソッドです。この doAction
メソッドの中でリクエストを解析し、どのポートレットを呼び出すかを決めます。ポートレットの呼び出しには、サーブレットレスポンスの sendRedirect
を使っています。
Pluto は、Portlet のレンダリングトリガーを <pluto:render/> タグで行っています。
ポートレットの呼び出し方法
ポートレットコンテナが、どうやってポートレットを呼び出すかですが、上に書いたようにサーブレットレスポンスの sendRedirect
を使っています。このリダイレクト URL のパラメータに、ポートレットコンテナが受け取ったリクエストパラメータ等の情報をエンコードして付与しています。
そして、呼び出されたポートレットプリケーション側で、待ち構えていた org.apache.pluto.core.PortletServlet
クラスがパラメータをデコードして、サーブレットリクエストを完成させます。
これで、ポートレットの呼び出しは完了です。
- クロスコンテキストを有効にする!
- ポートレットの呼び出しを行うには、クロスコンテキストを有効にしておかなければなりません。
ポータルは、異なるコンテキスト間のポートレットを呼び出します。そのため、コンテキストをまたいだ呼び出しが可能なように設定を行う必要があります。
Tomcat の場合、これは、クロスコンテキスト(crossContext)というパラメータで行います。{TOMCAT_HOME}/conf/server.xml に記述するコンテキストの設定に crossContext
を設定します。
エラーで困ったら
error.config.context.null
org.apache.pluto.PortletContainerRuntimeException: error.config.context.null
こんなエラーで困ったら、crossContext
を true に設定したかどうかを疑ってください。また、コンテキスト名が間違っていないか確認してください。
検討事項
TBD