ポートレットは、Web ポータルのための Java プラットフォーム・ベースのアプリケーションです。JSR-168 は、ポートレット・アプリケーションを開発するための Java Community Process 標準であり、ポートレットのライフ・サイクル管理、ポートレット・コンテナーの契約、パッケージ化、デプロイメント、その他のポータルに関連する側面について扱います。
Asynchronous JavaScript + XML、すなわち Ajax は、高度な対話式の Web アプリケーションを開発するための手法です。Ajax は、XML、HTML、DHTML、JavaScript、および DOM を組み合わせて使います。
ポートレットと Ajax は、両方ともユーザーに UI を表示するための手段として Web ブラウザーを使うようにフォーカスしているため、お互いに最適な組み合わせです。この 2 つを Java テクノロジーと組み合わせるには、DWR ライブラリーを使うと簡単です。DWR は、Ajax ベースの Web アプリケーションをビルドするための Java ライブラリーで、Apache ライセンスを受けたオープン・ソースです。DWR の基本的な目的は、Ajax の詳細を開発者から見えなくすることです。このため、サーバー・サイドで POJO (Plain Old Java Object) を使用すると、DWR は JavaScript プロキシー機能を動的に生成し、JavaScript によるクライアント・サイドの開発は JavaBeans を直接呼び出すような感じになります。DWR の主要コンポーネントは、ブラウザーからサーバーへの呼び出しを処理する Java サーブレットです。
この記事では、DWR を使って、3 つのポートレットを基にした Ajax アプリケーションのサンプルをビルドします。また、DWR をポートレット・アプリケーションと統合する方法を説明しますが、DWR がどのような動作をしているかの詳細にまでは触れません。DWR ライブラリーについて詳しくは、プロジェクトの Web サイトや developerWorks のページを確認してください (詳細については、参考文献を参照してください)。ここで説明するアプリケーションをビルドするには、バージョン 1.3 以降の Java プラットフォームと JSR-168 に準拠したポータル環境が必要です。このコードを開発およびテストするために、IBM Rational Application Developer V6.0、Apache Jetspeed 2.0 ポータル、および Java 5.0 で構成された環境を使用しました。
始めるにあたって、ポートレットと Ajax 開発を熟知している必要があります。これらのトピックについてさらに学びたい場合は、以下の参考文献セクションを確認してください。DWR を含むサンプル・アプリケーションの完全なコードは、ダウンロード・セクションからダウンロードできます。
サンプルのポートレット間メッセージング・アプリケーションをビルドする
サンプル・アプリケーションには、Orders、Order Details、および Customer Details の 3 つのポートレットがあります。図 1 は、サンプル・アプリケーションを示しています。
図 1. サンプル・アプリケーション
Orders ポートレットには、注文のリストがあります。ユーザーが注文番号をクリックすると、ポートレットが Order Details ポートレットと Customer Details ポートレットに注文番号を送信します。次に、これらのポートレットが、該当する注文と顧客の詳細を表示します。
ポートレットを開発する前に、開発環境と DWR をセットアップする必要があります。私は、Java ポートレット開発のサポートが組み込まれている IBM Rational Application Developer V6.0 を使用しますが、どのような開発環境でもかまいません。それでは、次のステップに従って始めましょう。
- 新しい JSR-168 ポートレット・プロジェクトを作成します。プロジェクトに InterPortletMessaging という名前を付けますが、ポートレットはまだ作成しないでください。
- dwr.jar (バージョン 1.1。リンクについては、参考文献を参照してください) をダウンロードして、/WebContent//WEB-INF/lib ディレクトリー内のプロジェクトに追加します。
- web.xml を開いて、リスト 1 のコードを追加します。これで、DWR サーブレットがアプリケーションに追加されます。このサーブレットは、バックグラウンドで要求を処理して、応答をブラウザーに戻します。
リスト 1. DWR サーブレット<servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping>
- リスト 2 のコードを使って、WEB-INF ディレクトリーに dwr.xml というファイルを作成します。これは、JavaScript で使用可能なクラスをコンテナーに伝える DWR 構成ファイルです。DWR はこの XML ファイルを読み取って、JavaScript コードを動的に生成し、Java クラスを JavaScript クラスで表します。これで、これらの Java クラスが提供する機能をブラウザーで使えるようになります。実際には、リスト 2 で参照されている Java クラスはまだ作成していませんが、すぐ後で作成します。
リスト 2. dwr.xml<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"> <dwr> <allow> <create creator="new" javascript="MessagingBean"> <param name="class" value="msg.MessagingBean"/> </create> </allow> </dwr>
これで、ポートレットで DWR を使用できるようになりました。ただし、サンプル・ポートレットを作成する前に、サンプル・アプリケーションのバックエンドの役割を果たす messaging Bean と mockup database Bean を作成する必要があります。
MockupDB と MessagingBean を作成する
リスト 3 に示されている MockupDB は、顧客からの注文のデータベースをシミュレートする singleton クラスです。すべての注文は、このクラスでハードコーディングされています。実際のアプリケーションはおそらくリレーショナル・データベース・システムを使用しますが、ここではこれで十分です。
リスト 3. MockupDB
package db;
import java.util.Hashtable;
import java.util.Map;
public class MockupDB {
private static MockupDB instance=new MockupDB();
private String[] orders=new String[4];
private Map orderDetails=new Hashtable();
private Map customerDetails=new Hashtable();
private MockupDB()
{
String ordStart="ORD";
orders[0]=ordStart+"000408015";
orders[1]=ordStart+"001600023";
orders[2]=ordStart+"000042000";
orders[3]=ordStart+"011235813";
orderDetails.put(orders[0],"1. WebSphere Everyplace Connection Manager<br/>"+
"2. WebSphere Portal");
orderDetails.put(orders[1],"1. DB2 Universal Database<br/>2. DB2 Everyplace");
orderDetails.put(orders[2],"1. Tivoli Access Manager for e-business <br/>2."+
"Tivoli Directory Integrator");
orderDetails.put(orders[3],"1. IBM System z9<br/>2. IBM System p5 550 Express");
customerDetails.put(orders[0],"<b>Systems and Technology Group</b><br/>"+
"Some Road<br/>Finland");
customerDetails.put(orders[1],"<b>Global Financing</b><br/>Another Street"+
"<br/>Finland");
customerDetails.put(orders[2],"<b>Software</b><br/>Yet Another Road"+
"<br/>Finland");
customerDetails.put(orders[3],"<b>Global Services</b><br/>Still Another "+
"Street<br/>Finland");
}
public static MockupDB getInstance()
{
return instance;
}
public String[] getOrders()
{
return orders;
}
public String getOrderDetails(String orderNro)
{
return (String)orderDetails.get(orderNro);
}
public String getCustomerDetails(String orderNro)
{
return (String)customerDetails.get(orderNro);
}
}
|
リスト 4 に示されている MessagingBean は、2 つのメソッドを持つ単純な POJO です。これらのメソッドは、注文番号を受け取って、それぞれ注文の詳細と顧客の詳細を戻します。MessagingBean は、MockupDB から詳細を取得します。
リスト 4. MessagingBean
package msg;
import javax.servlet.http.HttpSession;
import db.MockupDB;
public class MessagingBean {
public MessagingBean()
{
}
public String getOrderDetails(String orderNumber,HttpSession httpSession)
{
String orderDetails=MockupDB.getInstance().getOrderDetails(orderNumber);
httpSession.setAttribute("orderDetailsOrderNumber",orderNumber);
httpSession.setAttribute("orderDetails",orderDetails);
return orderDetails;
}
public String getCustomerDetails(String orderNumber,HttpSession httpSession)
{
String customerDetails=MockupDB.getInstance().getCustomerDetails(orderNumber);
httpSession.setAttribute("customerDetailsOrderNumber",orderNumber);
httpSession.setAttribute("customerDetails",customerDetails);
return customerDetails;
}
}
|
また、MessagingBean は、HttpSession に注文の詳細と顧客の詳細を追加します。
javaScriptFunctions.jsp は、DWR (engine.js) と動的に作成された MessagingBean.js ライブラリーから JavaScript ライブラリーをインポートします。MessagingBean.js は、dwr.xml (リスト 2) で紹介した Java Bean と同じ名前を使っていることに気づくでしょう。実際には、DWR が MessagingBean.js を生成します。DWR フレームワークは engine.js ライブラリーを使いますが、一般的には、開発者自身がこのライブラリーを直接使うことはありません。
リスト 5 で確認できるように、sendOrderNr() 関数は、リスト 4 で定義した MessagingBean 関数を呼び出します。DWR は、メソッド呼び出しに HttpSession を自動的に追加します。JavaScript 関数の最後のパラメーターは callback 関数です。この JSP は、次に作成するポートレット JSP に組み込まれています。
リスト 5. javaScriptFunctions.jsp
<%@ page contentType="text/html"
import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>
<SCRIPT type="text/javascript"
src='<%= renderResponse.encodeURL(renderRequest.getContextPath() +
"/dwr/interface/MessagingBean.js") %>'>
</SCRIPT>
<SCRIPT type="text/javascript"
src='<%= renderResponse.encodeURL(renderRequest.getContextPath() +
"/dwr/engine.js") %>'>
</SCRIPT>
<SCRIPT type="text/javascript">
function <portlet:namespace />sendOrderNr(orderNr)
{
document.getElementById("orderDetailsOrderNumber").innerHTML=orderNr;
document.getElementById("customerDetailsOrderNumber").innerHTML=orderNr;
MessagingBean.getOrderDetails(orderNr,<portlet:namespace />showOrderDetails);
MessagingBean.getCustomerDetails(orderNr,<portlet:namespace />showCustomerDetails);
return false;
}
function <portlet:namespace />showOrderDetails(orderDetails)
{
document.getElementById("orderDetails").innerHTML=orderDetails;
return false;
}
function <portlet:namespace />showCustomerDetails(customerDetails)
{
document.getElementById("customerDetails").innerHTML=customerDetails;
return false;
}
</SCRIPT>
|
バックエンド機能とプロキシー機能ができたので、ポートレット自体を開発できるようになりました。3 つのポートレットはすべて同じコードベースを使います。異なるのは、それぞれが使っている JSP の名前だけです。
- リスト 6 のコードを使用して新しいポートレットを作成して、Orders という名前を付けます。
リスト 6. Orders.javapackage interportletmessagingusingajax; import java.io.*; import javax.portlet.*; public class Orders extends GenericPortlet { // JSP folder name public static final String JSP_FOLDER = "/interportletmessagingusingajax/jsp/"; // JSP file name to be rendered on the view mode public static final String VIEW_JSP = "OrdersView"; public void init(PortletConfig config) throws PortletException{ super.init(config); } public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { // Set the MIME type for the render response response.setContentType(request.getResponseContentType()); // Invoke the JSP to render PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher( getJspFilePath(request, VIEW_JSP)); rd.include(request,response); //this is workaround for portletsession sharing between //servlets and portlets //see http://weblogs.java.net/blog/wholder/archive/2005/02/session_session.html //and http://mail-archives.apache.org/mod_mbox/portals-pluto-dev/200502.mbox/%3Ca //2519328f3ba1d1eddfc33c924b6805d@umich.edu%3E // PortletRequestDispatcher rd2 = getPortletContext().getRequestDispatcher("/dwr/"); rd2.include(request, response); } private static String getJspFilePath(RenderRequest request, String jspFile) { String markup = request.getProperty("wps.markup"); if( markup == null ) markup = getMarkup(request.getResponseContentType()); return JSP_FOLDER+markup+"/"+jspFile+"."+getJspExtension(markup); } private static String getMarkup(String contentType) { if( "text/vnd.wap.wml".equals(contentType) ) return "wml"; return "html"; } private static String getJspExtension(String markupName) { return "jsp"; } }
- OrdersView.jsp を (interportletmessagingusingajax/jsp/html ディレクトリーに) 作成して開き、リスト 7 のコードを追加します。
リスト 7. OrdersView.jsp<%@ page contentType="text/html" import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <jsp:include page="javascriptFunctions.jsp" /> <DIV style="margin: 6px"> <H4 style="margin-bottom: 3px">Orders</H4> <table cellspacing="0" cellpadding="5" border="1"> <% db.MockupDB database= db.MockupDB.getInstance(); String[] orders=database.getOrders(); for(int i=0;i<orders.length;i++) { %> <tr> <td><%="000000000"+String.valueOf(i+1) %></td> <td><a href="" onclick="return <portlet:namespace />sendOrderNr('<%= orders[i]%>');"><%=orders[i]%></a></td> </tr> <% } %> </table> </DIV>
- 2 番目のポートレットは OrderDetailsPortlet.java です。このポートレットではリスト 6 のコードを使って、VIEW_JSP 変数の値を OrdersDetailsPortletView.jsp に変更します。この JSP のコードはリスト 8 にあります。
リスト 8. OrdersDetailsPortletView.jsp<%@ page contentType="text/html" import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <DIV style="margin: 6px"> <H4 style="margin-bottom: 3px">Order details</H4> <table cellspacing="0" cellpadding="5" border="1"> <tr> <th>Order number</th> <th>Order details</th> </tr> <tr> <% String orderDetailsOrderNumber=(String)renderRequest.getPortletSession().getAttribute( "orderDetailsOrderNumber",PortletSession.APPLICATION_SCOPE); String orderDetails=(String)renderRequest.getPortletSession().getAttribute( "orderDetails",PortletSession.APPLICATION_SCOPE); if(orderDetailsOrderNumber==null) { orderDetailsOrderNumber=""; } if(orderDetails==null) { orderDetails=""; } %> <td><div id="orderDetailsOrderNumber"><%=orderDetailsOrderNumber%> </div></td> <td><div id="orderDetails"><%=orderDetails%></div></td> </tr> </table> </DIV>
- 3 番目のポートレットは CustomerDetailsPortlet.java です。このポートレットにはリスト 6 のコードを使用して、VIEW_JSP 変数の値を CustomerDetailsPortletView.jsp に変更します。この JSP のコードはリスト 9 にあります。
リスト 9. CustomerDetailsPortletView.jsp<%@ page contentType="text/html" import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <% %> <DIV style="margin: 6px"> <H4 style="margin-bottom: 3px">Customer details</H4> <table cellspacing="0" cellpadding="5" border="1"> <tr> <th>Order number</th> <th>Customer details</th> </tr> <tr> <% String customerDetailsOrderNumber= (String)renderRequest.getPortletSession().getAttribute( "customerDetailsOrderNumber",PortletSession.APPLICATION_SCOPE); String customerDetails=(String)renderRequest.getPortletSession().getAttribute( "customerDetails", PortletSession.APPLICATION_SCOPE); if(customerDetailsOrderNumber==null) { customerDetailsOrderNumber=""; } if(customerDetails==null) { customerDetails=""; } %> <td><div id="customerDetailsOrderNumber"><%=customerDetailsOrderNumber%> </div></td> <td><div id="customerDetails"><%=customerDetails%></div></td> </tr> </table> </DIV>
これでサンプル・アプリケーションの準備ができました。次は、ポートレットを WAR ファイルとしてパッケージにして、Apache Jetspeed ポータルでテストします。
このセクションでは、サンプル・アプリケーションの動作を確認します。最初に、ポートレット WAR を作成して、Jetspeed ポータルにインストールします。次に、ポートレットをポータルに追加して、動作を確認します。すべてを単一のページにビルドしますが、必要であれば複数のページに配置することができます。その場合も背後のメカニズムは機能します。
ポートレット・アプリケーションを Jetspeed にインストールする
ポートレット WAR ファイルを <Jetspeed install dir>/webapps/jetspeed/WEB-INF/deploy ディレクトリーにコピーして、WAR ファイルを Jetspeed にインストールします。すると、Jetspeed がポートレットを自動的にインストールします。これで、ポートレットを使うための準備ができました。
新規ページを Jetspeed ポータルに追加するには、次のステップを使用します。
- Jetspeed ポータルにアクセスして、管理者としてログインします。
- 図 2 に示すように、右側の隅にあるEdit アイコンをクリックして、Inter-Portlet Messaging という名前の新規ページを追加します。
図 2. 新規ページを追加する
-
Inter-Portlet Messaging ページを選択して、Edit アイコンをクリックします。次に、このページにポートレットを追加するには、Add a portlet アイコンをクリックします。Orders、Order Details、およびCustomer Details の各ポートレットを選択して、Select portlets をクリックしてポータル・ページにポートレットを追加します。これが完了すると、作成したページは図 3 のようになります。
図 3. ページ上のポートレット
図 4 に示す Orders ポートレットには、注文がリストされています。
図 4. Orders ポートレット
注文番号をクリックすると、他のポートレットに特定の注文の詳細が表示されます。Customer Details ポートレットには、図 5 に示すように顧客情報が表示されます。この情報は MockupDB から取得されます。
図 5. Customer Details ポートレット
また、Order Details ポートレットには、図 6 で確認できるように、MockupDB から取得された情報が表示されます。
図 6. Order Details ポートレット
必要であれば、前に戻って、ポートレットをさらに別のページに追加することもできます。ポートレットの内容はユーザー・セッションに保管されるため、ポートレットを単一のページに配置する必要はないことがわかると思います。
この記事では、Ajax を使ってポートレット間通信を行う方法を紹介しました。Ajax は、対話式の Web ページを開発するための非常に強力な手法です。また、Ajax 対応のポートレットでは、ポータルでよくありがちな、要求から応答までの遅延がなくなることで、ユーザー・エクスペリエンスが大幅に向上します。
この記事のサンプル・コードは、独自のアプリケーションを開発するための開始点として使うことができます。このコードは、DWR がどのようにして、Java プログラミング・モデルを Web ブラウザーでも使えるようにしているかも示しています。DWR を使うと、ブラウザーで JavaBeans を使うことができるかのように感じられます。DWR は、Ajax の詳細のほとんどすべてを見えなくすることで作業を単純化し、ユーザーが Ajax 開発の基本ではなく目先の作業に集中できるようにします。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Source code | j-ajaxportlet.war | 3MB | HTTP |
学ぶために
- 「Mastering Ajax」(Brett McLaughlin 著、developerWorks、2006 年) を読んで、Ajax プログラミングと概念を把握してください。
-
DWR: Easy Ajax for Java を読んで、パッケージの作成者による一連の参考資料を使って DWR を開始してください。
- 「Ajax for Java developers: Ajax with Direct Web Remoting」(Philip McCarthy 著、developerWorks、2005 年 11 月) には、DWR を使って JavaBeans メソッドを直接 JavaScript コードに公開し、Ajax の大変な作業を自動化する方法が示されています。
-
Build and test JSR 168-compliant portlets with Apache Pluto (Mark Talbot と Kulvir Singh Bhogal 共著、developerWorks、2006 年 4 月) には、簡単なポートレットをビルド、コンパイル、パッケージ化して、Pluto にデプロイして JSR 168 に準拠しているかどうかテストする方法が示されています。
-
developerWorks Web development は、さまざまな Web ベースのソリューションを網羅した記事を専門に扱っています。
- Stefan Hepper 著の「Comparing the JSR 168 Java Portlet Specification with the IBM Portlet API」(developerWorks、2003 年 12 月) を読んで、Java ポートレット開発についてさらに学んでください。
製品や技術を入手するために
-
Direct Web remoting をダウンロードしてください。これは、この記事で開発したアプリケーションを使用可能にするオープン・ソースの Java ライブラリーです。
-
Apache Jetspeed をダウンロードして、このポータルの詳細を調べてください。
-
Rational Application Developer for WebSphere Software V6.0 で、この開発環境の無料のトライアル・バージョンをダウンロードしてください。
