ポートレットの作成

ポートレットの作成

ここでは、JSR 168 に準拠したポートレットの作成を説明します。HelloWorld的なポートレットを作成して、作成したものを Jetspeed2 に追加してみます。

準備

早速、ポートレットを作っていきましょう。今回、作成するポートレットは、入力フォームを持ち、値を入力すると、その入力された値を表示するポートレットです。ビルドする上で、javac や ant などで実行すると、依存関係の解決など少々面倒なので、簡単にするためにも、maven でビルドするプロジェクトを作成します。まずは、ディレクトリを作成します。

$ cd
$ mkdir -p Projects/helloworld
$ cd Projects/helloworld/

この helloworld ポートレットプロジェクトでは、次のファイルを作成します。

project.properties
project.xml
src/webapp/WEB-INF/portlet.xml
src/webapp/WEB-INF/web.xml
src/webapp/WEB-INF/view/helloworld.jsp
src/java/com/marevol/portlet/helloworld/resources/HelloWorldResources_ja.properties
src/java/com/marevol/portlet/helloworld/resources/HelloWorldResources.properties
src/java/com/marevol/portlet/helloworld/HelloWorldPortlet.java

project.properties および project.xml は、Maven のプロジェクトファイルです。そして、それ以外のファイルが、ポートレットを構築・実行する上で必要になるファイルになります。helloworld ポートレットに関する一連のファイルは、ここ からダウンロードできます。

Mavenプロジェクトファイル

それでは、順に構成ファイルについてみていきましょう。

まずは、プロジェクトを Maven で利用できるようにするための project.properties と project.xml です。これらのファイルについては、通常の Maven プロジェクトと同様に作成しています。ですので、ポートレット用に特殊な設定などはしていません。

project.properties には、maven.xdoc.dateプロパティを設定していますが、maven xdoc を実行しなければ、利用されることはないので、今回のポートレットのビルドには一切関係はありません。ですので、project.propertiesには何も設定されていないのと同じと考えて良いでしょう。

./project.properties
# -------------------------------------------------------------------
# Copyright 2001-2004 The Apache Software Foundation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -------------------------------------------------------------------
maven.xdoc.date=left
## maven.xdoc.version=${pom.currentVersion}

projext.xml は、普通の Maven プロジェクトのものと同様に記述しています。ポートレット用にビルドするために、依存関係に portlet-api を指定しておけば、ポートレットを作成することができます。あとは、今回の helloworld ポートレットでは、ビューに JSP を利用して、JSTL を利用しているので、それに必要な依存関係を追加しています。もし、独自にポートレットを作成する場合には、必要なライブラリがあれば、追加してください。

./project.xml

<project>
<pomVersion>3</pomVersion>
<id>helloworld</id>
<name>Hello World Portlet</name>
<currentVersion>1.0</currentVersion>
<organization>
<name>MAREVOL.COM</name>
<url>http://www.marevol.com/</url>
</organization>
<inceptionYear>2002</inceptionYear>
<package>com.marevol.portlet.helloworld</package>
<description>Sample Portlet to say "Hello" to you</description>
<repository/>
<mailingLists/>
<developers>
<developer>
<name>Shinsuke Sugaya</name>
<id>shinsuke</id>
<email>shinsuke at yahoo.co.jp</email>
<roles>
<role>Java Developer</role>
</roles>
<timezone>+9</timezone>
</developer>
</developers>
<dependencies>
<dependency>
<id>portlet-api</id>
<groupId>portlet-api</groupId>
<version>1.0</version>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<id>jstl</id>
<version>1.1.2</version>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/java</sourceDirectory>
<unitTestSourceDirectory>src/test</unitTestSourceDirectory>
<unitTest>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/NaughtyTest.java</exclude>
</excludes>
</unitTest>
<resources>
<resource>
<directory>src/java</directory>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>

ポートレットの構成ファイル

ここからがポートレットを作成する上で必要になってくるファイルです。基本的には、Servlet Specification 2.3, SRV.9 で定義されている Web アプリケーションの構成と同じで、サーブレット、JSP、HTMLページ、クラスなどのファイルを含み、それらに加えて、ポートレットのクラスファイルとポートレット配備記述子(portlet.xml)を含みます(簡単に言うと、普通に Java で Web アプリケーションを作るときと同じで、portlet.xml が追加されたものです)。ポートレットアプリケーションについては、JSR 168 の PLT.19 で定義されています。

まず、portlet.xml について見てみましょう。portlet.xml は、ポートレットのリソースについて定義しています。ですので、ポートレットの構成情報などがいろいろと記述されています。portlet.xml のスキーマなどの詳細の情報は、JSR 168 の PLT.21.5 や PLT.21.6 を参照してみてください。

portlet.xml では、ルートノードに portlet-app 要素があり、その下に portlet 要素を複数定義できます。1つのポートレットの定義が1つの portlet 要素に対応しているので、portlet.xml に portlet 要素を複数記述することで、複数のポートレットを定義することもできます。

次に、portlet 要素の下にある要素について見ていきましょう。portlet-name 要素は、ポートレットの名前になります。これは、このポートレットアプリケーション内でユニークな値である必要があります。display-name 要素と、次の description 要素は、ポートレットに関する簡単な名前と説明になります。これらの値は、たとえば、ポータルサーバーのポートレット管理ツールなどで表示される値になります。そのツール上で日本語の名前を出したいような場合には、xml:lang 属性を追加して、記述します。portlet.xml では、display-name と description については、xml:lang 属性を用いて、複数定義することができます。portlet-class 要素は、実行するポートレットのクラス指定します。expiration-cache 要素は、キャッシュの有効期限を秒単位で指定します。-1 は無期限を意味します。supports 要素では、サポートする MIME タイプやポートレットモードを指定しています。supported-locale 要素は、サポートしているロケールを記述します。resource-bundle 要素では、ポートレットで利用するリソースバンドルを指定します。指定したリソースバンドルは、PortletConfig#getResourceBundle() で取得することができます。 portlet-info 要素では、ポートレットで表示されるタイトルなどを指定しています。タイトルを国際化するためには、resource-bundle 要素で指定したリソースバンドル内に記述する必要があります。

./src/webapp/WEB-INF/portlet.xml

<portlet-app id="helloworld" version="1.0">
<portlet id="HelloWorld">
<portlet-name>HelloWorld</portlet-name>
<display-name>Hello World</display-name>
<display-name xml:lang="ja">ハローワールド</display-name>
<description>HelloWorld is a portlet for testing</description>
<description xml:lang="ja">HelloWorld はテスト用ポートレットです</description>
<portlet-class>com.marevol.portlet.helloworld.HelloWorldPortlet</portlet-class>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<supported-locale>ja</supported-locale>
<resource-bundle>com.marevol.portlet.helloworld.resources.HelloWorldResources</resource-bundle>
<portlet-info>
<title>Hello World</title>
<short-title>This is a portlet for testing</short-title>
<keywords>Hello,Test</keywords>
</portlet-info>
</portlet>
</portlet-app>

web.xml は、通常の Web アプリケーションと同様に、Web アプリケーションのリソースなどを指定しています。今回は、特に指定することもないので、display-name と description 要素にこのポートレットについてを記述するだけになっています。

./src/webapp/WEB-INF/web.xml


"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>HelloWorld Portlet</display-name>
<description>HelloWorld Portlet</description>
</web-app>

HelloWorldPortlet は、portlet.xml で portlet-class 要素で指定したポートレットのクラスです。ポートレットにアクセスすると、このクラスが呼ばれ、必要な処理を実行します。ポートレットは、 javax.portlet.Portletインターフェース実装したクラスになります。今回は、そのインターフェースを実装した javax.portlet.GenericPortlet を継承して作成されます。また、Jetspeed2 には、GenericPortlet を拡張した GenericServletPortlet や GenericVelocityPortlet などのクラスもいくつかあるので、必要に応じて利用するとポートレットの作成が楽になるかもしれません(今回は、それらのクラスについてはこれ以上ふれません。そのうち機会があれば、GenericVelocityPortlet などを説明するかもしれません。ビューに Velocity を使うと結構、楽な気がするので・・・)。今回は、スタンダードな方法で GenericPortlet を利用しています。

この HelloWorldPortlet は、init, doView, processAction3つのメソッドを持っています。まず、init はその名の通り、ポートレットの初期化を行います。たとえば、portlet.xml で値を与えている場合などには、ここで取得するのが良いでしょう。今回は、特に何もすることがないので、親に渡しているだけです。

次に doView と processAction ですが、Portletインターフェースには、processActionとrenderメソッドが定義されています。つまり、ポートレットの処理は、アクションの処理と表示の処理が完全に分かれていて、processAction でアクションの処理を行い、表示などは、render メソッドで行われます。GenericPortletの render メソッドでは、ポートレットのタイトルを設定して、doDispatchメソッドに渡されています。そして、その doDispatch メソッドでポートレットのモードによって、処理を振り分けています。たとえば、普通に表示する表示モードでは doView、ポートレットの右上にある編集ボタンを押したときなどになる編集モードの描画処理を行う場合は doEdit などです。今回のポートレットでは、表示モードしか扱わないので、doView だけを追加しておきます。

doView メソッドでは、RenderRequest と RenderResponse を利用し、まず、コンテンツタイプを設定してから(コンテンツタイプを指定しないと Exception が投げられます)、与えられた YOUR_NAME_KEY キーの値を取得して、helloworld.jsp に渡す処理をしています。

processAction では、ActionRequest と ActionResponse を持っています。このメソッドでは、helloworld.jsp で送信された値を ActionResponse に渡しているのがわかります。ところで、このRender~やAction~は何なのでしょうか?これらは、名前の通り、表示のためのRender~であり、アクションを処理するためのAction~です。つまり、processAction で jsp などからの様々な入力値を処理して、ActionResponse の setRenderParameter で RenderRequest に渡します。つまり、jsp からの入力は、doView の RenderRequest で取得できないということになります。ですので、doView などでは、本当に表示だけの処理に専念することになります。これの意味を考えるためには、次のような状況を考えてください。ポートレットは、1つのページに複数のポートレットを表示することになります。各ポートレットは独立していて、ほとんどの場合、複数の中の1つだけに入力データなどを送信して処理することになります。その対象のポートレットでは、processAction が呼ばれ、doView が呼ばれることになるでしょう。それ以外のポートレットではどうでしょうか?他のポートレットでは、ページが更新されますが、表示内容はその更新に関係なく、同じ内容を表示しなければなりません(たとえば、他のページで一覧を表示していて見ていたのに、他のポートレットで処理したらメニューに戻るなどあったら困ります)。ですので、ページが更新されたとしても、アクションの処理の対象でなければ、表示の内容を維持する必要があるので、アクションに関係なく、Render~で独立したパラメータを持つことになります。こういうことから、processAction の Action~からデータを取得して処理し、表示用のパラメータを設定して、setRenderParameterなどで、Render~に渡すという処理の流れになります。

./src/java/com/marevol/portlet/helloworld/HelloWorldPortlet.java
/*
* Copyright 2000-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.marevol.portlet.helloworld;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
/**
* HelloWorldPortlet is a portlet to say "Hello!"
*
* @author Shinsuke Sugaya
*/
public class HelloWorldPortlet extends GenericPortlet
{
public static final String YOUR_NAME_KEY = "yourName";
/* (non-Javadoc)
* @see javax.portlet.Portlet#init(javax.portlet.PortletConfig)
*/
public void init(PortletConfig config) throws PortletException
{
super.init(config);
}
/* (non-Javadoc)
* @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
*/
protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException
{
response.setContentType("text/html");
PortletContext context = getPortletContext();
String yourName = request.getParameter(YOUR_NAME_KEY);
if (yourName == null)
{
yourName = "";
}
request.setAttribute(YOUR_NAME_KEY, yourName);
PortletRequestDispatcher rd = context.getRequestDispatcher("/WEB-INF/view/helloworld.jsp");
rd.include(request, response);
}
/* (non-Javadoc)
* @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse)
*/
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException
{
String yourName = request.getParameter(YOUR_NAME_KEY);
if (yourName != null)
{
response.setRenderParameter(YOUR_NAME_KEY, yourName);
}
}
}

helloworld.jsp は、今回のポートレットのビューの部分にあたります。この JSP ファイルは、HelloWorldPortlet クラスから呼ばれて、実行されます。ここでは、入力フォームを表示して、送信ボタンをクリックすると、そのメッセージが表示される処理を実行します。

この helloworld.jsp では、2つのタグライブラリを読み込んでいますが、ポートレットで利用されるのが http://java.sun.com/portlet のタグライブラリです。taglib uri=… でこのタブライブラリを宣言しておいて、<portlet:defineObjects/> を記述すると、それ以降で、renderRequest などのパラメータを利用することができるようになります。また、このタグライブラリには、<portlet:actionURL /> などの URL を作成するタグもあるので、ビューで JSP を利用するときには、通常、このタブライブラリは使うことになると思います。このタグライブラリに関する詳細は、PLT.22 を参照してください。

fmt のタグライブラリの方は、一般的な JSTL のものですので、そちらの方のドキュメントなどを参照してください。今回は、メッセージを国際化するためだけに利用しています。

./src/webapp/WEB-INF/view/helloworld.jsp
<%--
Copyright 2004 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    • %>
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %> <fmt:setBundle basename="com.marevol.portlet.helloworld.resources.HelloWorldResources" /> <portlet:defineObjects/>
<fmt:message key="helloworld.lable.Hello"/> <%= renderRequest.getAttribute("yourName") %>
<fmt:message key="helloworld.lable.YourName"/>
"/>

最後に、HelloWorldResources プロパティファイルについてです。今回は、デフォルトのものと、…_ja.properties の日本語用のものを作成しています。このリソースは、portlet.xml と helloworld.jsp で指定されています。

デフォルトのものと日本語用を比べると、日本語用には、javax.portlet で始まるプロパティが追加されているのがわかると思います。これらは、portlet.xml の portlet-info 要素以下にあったものにそれぞれが対応しています。たとえば、ポートレットの上部に表示されるタイトルで日本語のタイトルを表示したいといった場合などは、javax.portlet.title プロパティを portlet.xml の resouce-bundle 要素で指定したプロパティふぃあるに追加しておけば、このタイトルが表示されます。short-title と keywordsも同様に、日本語が表示したい場合には、プロパティファイルに加えておきます(short-title はその名の通り、本来は、短いタイトルです。ここでは、長くなってしまっていますが・・・。これはたとえば、携帯電話などで通常より短いタイトルが良いような場合に利用されることになると思います)。

./src/java/com/marevol/portlet/helloworld/resources/HelloWorldResources_ja.properties
# Copyright 2004 The Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# $Id: HelloWorldResources_ja.properties,v 1.2 2004/09/24 06:14:57 shinsuke Exp $
#
# portlet info
javax.portlet.title=\u30CF\u30ED\u30FC\u30EF\u30FC\u30EB\u30C9
javax.portlet.short-title=\u3053\u308C\u306F\u3001\u300C\u3053\u3093\u306B\
\u3061\u306F\u300D\u3068\u8A00\u3046\u30DD\u30FC\u30C8\u30EC\u30C3\u30C8\u3067\
\u3059\u3002
javax.portlet.keywords=\u30C6\u30B9\u30C8,\u30CF\u30ED\u30FC
# helloworld.jsp
helloworld.lable.Hello=\u3053\u3093\u306B\u3061\u306F\u3001
helloworld.lable.YourName=\u540D\u524D:
helloworld.lable.Submit=\u9001\u4FE1
./src/java/com/marevol/portlet/helloworld/resources/HelloWorldResources.properties
# Copyright 2004 The Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# $Id: HelloWorldResources.properties,v 1.2 2004/09/24 06:14:57 shinsuke Exp $
#
# helloworld.jsp
helloworld.lable.Hello=Hello!
helloworld.lable.YourName=Your Name:
helloworld.lable.Submit=Submit

ポートレットのビルド

必要なものが一通り用意できたので、ビルドして、ポートレットアプリケーションの war ファイルを作成してみましょう。ビルド方法は、非常に簡単です(もちろん Maven を実行できる環境が用意済みであることを想定しています)。

$ ls
project.properties  project.xml  src
$ maven war
__  __
|  \/  |__ _Apache__ ___
| |\/| / _` \ V / -_) ' \  ~ intelligent projects ~
|_|  |_\__,_|\_/\___|_||_|  v. 1.0.2
.
.
.

Maven の war プラグインの war ゴールをそのまま利用できるので、それを使用しています。このことからも、基本的には、普通の Web アプリケーションと同じであることがわかると思います。ビルドに成功していると、target ディレクトリの下に helloworld.war が作成されていると思います。

$ ls target/helloworld.war
target/helloworld.war

これで、JSR 168 に準拠しているポートレットを作成できたことになります。これを、Jetspeed に限らず、JSR 168 に準拠しているポータルサーバー上に持っていき、そのポータルサーバーの配備手順に従って、配備すれば、動きます。

Jetspeed2 への配備

配備するポートレットが作成できたので、早速、Jetspeed2 上で動かすことにしましょう。Jetspeed2 では、どのページにどのポートレットを表示するかという情報は、PSML ファイルに記述されています。ですので、helloworld ポートレットを表示するページを作成する必要があります(Portal Site Manager を使えば、できるようになると思うのですが、現在、バグがあるっぽいのと、管理系のポートレットを変えるような動きがあるので、手動でファイルを置くことにします)。

まず、Jetspeed2 を Tomcat へ配備して、Tomcat の startup.sh をすれば、Jetspeed2 が起動できる状態にしてください(ビルド方法でいうと、maven quickStart までが完了した状態)。準備ができたら、helloworld ポートレットを表示するためにページを作成します。作成するファイルは、webapps/jetspeed/WEB- INF/pages/helloworld.psml になります。

$ cd
$ cd apache/jakarta-tomcat-5.0.30
$ vi webapps/jetspeed/WEB-INF/pages/helloworld.psml

ファイルの内容は以下のようになります。ルートノードが page 要素で、以下にある defaults 要素の造成でポートレットとページ自体のデザインを決めています。Jetspeed2 ではデフォルトのデザインは、tigris デコレータを適用しています。

ページのタイトルは、英語環境でアクセスすると、Hello World for Jetspeed 2 が表示されますが、日本語環境で表示すると、Jetspeed2用ハローワールドと表示されます。

helloworldポートレットは、fragment 要素の id が hw-11 で指定されたところに定義されています。fragment要素の ID が hw-1 の jetspeed-layouts ポートレットは、その名前の通り、レイアウトを管理していて、このポートレットが、それ以下の要素にあるポートレットのレイアウトを決めています。

<page>
<defaults
skin="orange"
layout-decorator="tigris"
portlet-decorator="tigris"
/>
<title>Hello World for Jetspeed 2</title>
<metadata name="title" xml:lang="ja">
Jetspeed2用ハローワールド
</metadata>
<fragment id="hw-1" type="layout" name="jetspeed-layouts::VelocityOneColumn">
<fragment id="hw-11" type="portlet" name="helloworld::HelloWorld">
<property layout="OneColumn" name="column" value="0" />
</fragment>
</fragment>
<security-constraints>
<security-constraints-ref>public-view</security-constraints-ref>
</security-constraints>
</page>

では、ページを作成したところで、Jetspeed2 を起動してみましょう。

$ ./bin/startup.sh

Jetspeed2 が起動したら、http://localhost:8080/jetspeed/portal/helloworld.psml にアクセスしてみてください。どうでしょう?エラーが表示されているのではないでしょうか?そうです。まだ、helloworld.war を Jetspeed2 に配備していないので、エラーが表示されます。ということですので、helloworld.war を配備しましょう。

Jetspeed2 のポートレットの配備は、非常に簡単で、/WEB-INF/deploy/ に war ファイルを置くだけです。そのディレクトリに置いておけば、しばらくすると自動的にJetspeed2 に配備されます。早速、コピーしてみましょう。

$ cp ~/Projects/helloworld/target/helloworld.war webapps/jetspeed/WEB-INF/deploy/

コピーしたら、しばらく待ち、再度、先ほどのページにアクセスしてみてください。今度は、どうでしょう?表示されたでしょうか?このように、 Jetspeed2 でのポートレットの配備は、動的に行ってくれるので、非常に簡単です。配備を解除したい場合は、admin でログインして、PLAM ポートレットで行うことができます。PLAM ポートレットでは、現在、配備中のポートレットも確認できるので、一度、見てみると良いと思います。

JSR 168 準拠のポートレットの作成と、Jetspeed2 への配備方法を一通り、見てきました。これを機にいろいろなポートレット作成にぜひ、チャレンジしてみてください♪

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です