レベル: 初級 Manu T. George, Staff Software Engineer,
IBM
Vamsavardhana Reddy Chillakuru, Advisory Software Engineer,
IBM
2008年 09月 24日 この記事では WebSphere® Community Edition 2.1 に含まれる Ajax 用の機能とフレームワークの一部を簡単に紹介します。また Web 2.0 のサンプル・アプリケーションを作成する手順を説明しながら、これらのフレームワークを使うことによって開発が容易になることを示します。
はじめに
Ajax (Asynchronous JavaScript and XML) は RIA (Rich Internet Application) の作成を可能にする一連の技術を表すために考え出された用語です。これらの技術を使うことによって、デスクトップ・アプリケーションと同様に高い応答性とリッチなユーザー・インターフェースを持つ Web アプリケーションを作成することができます。またその Web アプリケーションは表示されているページに影響を与えずに、HTML ページ全体ではなくデータを要求するだけで、バックグラウンドで非同期にデータを取得することができます。このバックグラウンドで行われる非同期通信のためには、最近のブラウザーに用意されている XmlHttpRequest または同等のオブジェクトを使います。
IBM WebSphere Application Server Community Edition v2.1.x (以下、Community Edition と呼びます) には、Ajax 対応のアプリケーションの作成やホスティングによく使用されるフレームワークがいくつかパッケージされています。この記事では、そうしたフレームワークのうち、次の 3 つのフレームワークの構成方法と使用方法について調べます。
- Dojo ツールキット
- DWR (Direct Web Remoting)
- Tomcat の Comet サポート
また、これらの技術を使うことによってユーザー・エクスペリエンスを改善した簡単な Web アプリケーションを作成します。この記事では WebSphere Application Server Community Edition v2.1.x が必要になります。
Dojo ツールキット
Dojo はオープンソースの DHTML ツールキットであり、JavaScript で作成されています。Dojo によって、ブラウザー特有の振る舞いを処理するなど、JavaScript を使う際の問題の一部を解決することができます。こうしたブラウザー特有の振る舞いは Dojo ツールキットによって抽象化され、ユーザーから見えなくなります。
また Dojo には、動的でブラウザーに依存しない Web ページを迅速に作成するために使うことができる、一連の構成可能なウィジェットも用意されています。JavaScript API で構成される Dojo ツールキットは、WebSphere Application Server Community Edition v 2.1 に含まれています。Dojo ツールキットへのアクセスはコンテキスト・ルート /dojo から行います。また Dojo には、ページ全体を更新せずにサーバーからデータを取得するための非同期 XMLHttpRequest 呼び出しを行える API も用意されています。
DWR (Direct Web Remoting)
DWR を利用すると、JavaScript によるプロキシーをとおしてサーバー・サイドの Java™ オブジェクトをクライアントに公開することができます。DWR は公開されるすべての Java™ オブジェクトに対してプロキシーを作成し、クライアントはこのプロキシーを呼び出すことができます。すると DWR はサーバーの中の対応する Java メソッドを呼び出し、呼び出し側スクリプトに対して JSON (JavaScript Object Notation) フォーマットでレスポンスを返します。
また、同期、あるいは非同期にバックグラウンドでサーバーを呼び出すように DWR を構成することができるため、通常の HTTP によるリクエスト・レスポンス・モデルに伴うページの更新を回避することができます。
DWR にはさらに、サーバーからブラウザーに非同期で情報を送信するメカニズムである Reverse Ajax も用意されています。このメカニズムを利用すると、サーバーはクライアントに対して定期的な間隔でレスポンスをパブリッシュすることができますが、その方法には次の 3 つがあります。
- ポーリング: クライアントが一定の間隔でサーバーをポーリングします。
- Comet: クライアントがリクエストを行うと、サーバーはレスポンスのハンドルを保持し、そのハンドルの中に書き込みを続けます。この場合にはクライアントがポーリングする必要はありません。
- ピギーバック: レスポンスはまとめられ、クライアントからの次のリクエストに対するレスポンスと共に送信されます。
Tomcat の Comet サポート
Comet は Dojo Foundation の Alex Russel による造語であり、HTTP プロトコル上でのイベント駆動によるサーバー・プッシュ・メカニズムを表しています。通常の HTTP 通信では、常にクライアントがサーバーへの接続を開いてリクエストを送信することで、データ転送を開始します。サーバーはこのリクエストを処理し、同じ接続上でレスポンスを送信し、その接続を閉じます。そのため、接続が開かれている時間は比較的短時間です。Comet では、サーバーがその接続を開いたままに保ち、関係するイベントが発生するとデータの書き込みを続けます。Tomcat は NIO コネクターと APR コネクターによって Comet をサポートしています。BIO コネクターは Comet をサポートしていません。
NIO コネクターを作成する
デフォルトでは、Community Edition には NIO コネクターがプリインストールされていません。そのため Comet プロトコルを使用するアプリケーションを実行する前に、管理コンソールで新しい NIO コネクターを作成して起動する必要があります。まずポート 8080 で実行されている BIO コネクターを削除し、それから同じポート 8080 で実行される NIO コネクターを作成します。
- NIO コネクターの作成は次の手順で行います。
- Community Edition を起動し、ブラウザーで
https://localhost:8443/console/ を開きます。
- ユーザー名に
system と入力し、パスワードに manager と入力します。Login をクリックすると管理コンソールに Welcome ページが表示されます。
- 左側のナビゲーション・ペインで Web Server リンクをクリックすると、Web Server Manager のページが表示されます (図 1)。
図 1. Web Server Manager
- TomcatWebConnector という名前のコネクターを削除します。
- Add New セクションで Tomcat NIO Connector リンクをクリックすると、図 2 の画面が表示されます。
図 2. 新しい Tomcat NIO コネクターを追加する
- uniqueName フィールドの値として
TomcatNIOConnector を入力し、Save をクリックすると、NIO コネクターが作成されて起動されます。このウィンドウは図 3 のようになるはずです。
図 3. ネットワーク・リスナーに追加された TomcatNIOConnector
Web アプリケーションを作成する
これで Web アプリケーションを作成する準備がすべて整いました。このアプリケーションは株価情報表示アプリケーションで、5 秒ごとに更新されて最新の株価が表示されます。ここでは Tomcat が提供する Comet の実装に対して、DWR の Reverse Ajax 機能を使います。株価を表示するためには、Dojo ツールキットの中にある既製の Dojo ウィジェットである dojox.Grid ウィジェットを使います。また、Tomcat の Comet サポートを利用して株価情報を出力するサーブレットも作成します。
Comet サーブレットを作成する
Apache Tomcat には org.apache.catalina.CometProcessor インターフェースが用意されており、作成するすべてのサーブレットはこのインターフェースを実装することで、Comet サポートを利用することができます。このインターフェースによって public void event(CometEvent event) という 1 つのメソッドを定義し、これから作成するサーブレットに実装します。リスト 1 は、このサーブレットの 2 つの主なメソッドを示しています。
リスト 1: Comet サーブレット
public void init() throws ServletException {
super.init();
Runnable r = new Runnable() {
public void run() {
while (!stop) {
synchronized (openConnections){
List<Stock> stocks = new StockService().getStocks();
for (HttpServletResponse response : openConnections) {
try {
PrintWriter pw = response.getWriter();
for (Stock stock : stocks) {
pw.println(stock.getSymbol() + ":"
+ stock.getPrice());
}
pw.flush();
} catch (IOException e) {
e.printStackTrace(System.out);
}
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(System.out);
}
}
}
};
Thread stockThread = new Thread(r);
stockThread.setDaemon(true);
stockThread.start();
}
public void event(CometEvent event) throws IOException, ServletException {
HttpServletRequest request = event.getHttpServletRequest();
HttpServletResponse response = event.getHttpServletResponse();
if (event.getEventType() == CometEvent.EventType.BEGIN) {
synchronized (openConnections){
openConnections.add(response);
}
} else if (event.getEventType() == CometEvent.EventType.ERROR) {
synchronized (openConnections){
openConnections.remove(response);
}
} else if (event.getEventType() == CometEvent.EventType.END) {
synchronized (openConnections){
openConnections.remove(response);
}
} else if (event.getEventType() == CometEvent.EventType.READ) {
}
}
|
init メソッドの中でスレッドをインスタンス化していますが、このスレッドは株価を取得し、リッスンしているすべてのクライアント (つまり openConnections リストの中にあるすべてのレスポンス・オブジェクト) にその株価を書き込みます。event メソッドは、4 つのイベント (BEGIN、ERROR、END、READ) のうちのいずれかが発生すると呼び出されます。レスポンスは、パラメーターとして渡される org.apache.catalina.CometEvent オブジェクトから取得します。BEGIN イベントの場合には、レスポンス・オブジェクトのコレクションにレスポンスを追加します。ERROR イベントと END イベントの場合には、そのレスポンスをリストから削除します。
BEGIN イベントは、その接続に対する処理が開始されたことを表しています。そこで、そのレスポンスをレスポンスのリストに追加します。END イベントと ERROR イベントは接続が終了したこと、あるいはエラーが発生したことを表しています。これらのイベントの意味に関する詳細は Apache Tomcat のドキュメントを参照してください。
DWR を構成する
このアプリケーションには、このアプリケーションで表示される一連の株価がハードコーディングされています。株価は com.dev.trade.service.StockQuoteGenerator クラスによってランダムに更新されます。リスト 2 はこのクラスのソース・コードの関係する部分を示しています。
リスト 2: 株価を生成するクラス
package com.dev.trade.service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Collections;
import com.dev.trade.bo.Stock;
public class StockQuoteGenerator {
private StockService ss = new StockService();
private List<Stock> stocks = null;
private static StockQuoteGenerator generator = new StockQuoteGenerator();
private Set<QuoteListener>
listeners = Collections.synchronizedSet(new HashSet<QuoteListener> ());
private volatile boolean contextDestroyed = false;
private StockQuoteGenerator() {
Runnable r = new Runnable() {
public void run() {
try {
while (!contextDestroyed) {
update(generateStockQuotes());
Thread.sleep(5000);
}
}catch (Exception e) {
e.printStackTrace(System.out);
}
}
};
Thread t = new Thread(r);
t.setDaemon(true);
t.start();
}
public static StockQuoteGenerator getInstance() {
return generator;
}
public void setContextDestroyed(boolean cd) {
this.contextDestroyed = cd;
}
public void addListener(QuoteListener listener) {
listeners.add(listener);
}
private void update(List<Stock> stocks) {
for (QuoteListener listener : listeners) {
listener.updateStockQuotes(stocks);
}
}
private List<Stock> generateStockQuotes() {
stocks = ss.getStocks();
return stocks;
}
}
|
シングルトン・クラスである StockQuoteGenerator クラスのコンストラクターの中で新しいスレッドを開始し、そのスレッドに Runnable オブジェクトを割り当てています。そのため、このオブジェクトの run メソッドは contextDestroyed 変数が真に設定されるまで実行を続けます。この変数が真に設定されるのは、アプリケーションのコンテキストが破棄されようとする時のみです。そのために、web.xml の中にコンテキスト・リスナー (つまり com.dev.trade.listener.ServletContextListener) を登録します。
generateStockQuotes メソッドは StockService の getStocks メソッドを呼び出し、生成された株価を取得します。リスナーを追加するためには addListener メソッドを使います。これらのリスナーは com.dev.trade.service.QuoteListener インターフェースを実装し、ひいては updateStockQuotes メソッドを実装します。
リスナーは、どれも com.dev.trade.service.StockQuoteListenerImpl のインスタンスです。このクラスはこのクラスのコンストラクターの中で、DWR が提供する org.directwebremoting.WebContext クラスのインスタンスを初期化し、そしてこのクラス自身を StockQuoteGenerator のリスナー・リストに登録します。リスト 3 はこのクラスの updateStockQuotes メソッドを示しています。
リスト 3: さまざまなクライアントにデータをプッシュするためのコード
public void updateStockQuotes(List<Stock> stocks) {
ScriptBuffer script = new ScriptBuffer();
script.appendScript("updateStocks(").appendData(stocks).appendScript(
");");
Collection<ScriptSession> sessions = wctx.getAllScriptSessions();
for (ScriptSession session : sessions) {
if(!session.isInvalidated()){
session.addScript(script);
} else {
wctx.getAllScriptSessions().remove(session);
}
}
}
|
このメソッドは、サーバーとの接続が開いているすべてのクライアントに対して JavaScript による関数呼び出しを伝播します。このメソッドは updateStocks 関数を呼び出し、株価の値を渡します。実際には、この JavaScript メソッドは表示対象としての Dojo のグリッド・ウィジェットのモデルを更新し、次にそのモデルの更新メソッドを呼び出します。それによってグリッドには新しいデータが表示されます。DWR は株価情報表示アプリケーションの開いている各ページに対するスクリプト・セッションを作成します。
このような動作をするためには、StockQuoteListenerImpl クラスのコンストラクターの呼び出しは DWR コードが実行されているスレッドと同じスレッドによって行われなければなりません。それには、StockQuoteListenerImpl のオブジェクトをインスタンス化するように DWR を構成する必要があります。さらにそのような構成をするには、dwr.xml ファイルを作成し、アプリケーションの WEB-INF ディレクトリーの中に配置します。リスト 4 はこのファイルを示しています。
リスト 4: dwr.xml
<dwr>
<allow>
<create creator="new" javascript="Ticker" scope="application">
<param name="class" value="com.dev.trade.service.StockQuoteListenerImpl"/>
</create>
<create creator="new" javascript="StockService" scope="application">
<param name="class" value="com.dev.trade.service.StockService"/>
</create>
<convert converter="bean" match="com.dev.trade.bo.Stock"/>
</allow>
</dwr>
|
StockQuoteListenerImpl が creator によって dwr.xml の中で公開されていることに注目してください。アプリケーションごとに必要なインスタンスは 1 つのみであるため、この StockQuoteListenerImpl オブジェクトのスコープを application に設定します。JavaScript に公開する、もう一方のクラスは、株価リストを取得するための getStocks メソッドを提供する com.dev.trade.service.StockService です。また、アプリケーションの web.xml には DWRServlet を追加する必要があります。それによって DWR が初期化され、上のように構成されたオブジェクトのプロキシーが DWR によって JavaScript に公開されます。リスト 5 は web.xml ファイルを示しています。
リスト 5: web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" version="2.4"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>brokerage</display-name>
<listener>
<listener-class>
com.dev.trade.listener.DwrContextListener
</listener-class>
</listener>
<servlet>
<display-name>DWR Servlet</display-name>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>initApplicationScopeCreatorsAtStartup</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/stocks.html</welcome-file>
</welcome-file-list>
</web-app>
|
DWR が Comet を使って Reverse Ajax を使用できるように、activeReverseAjaxEnabled プロパティーを真に設定します。偽に設定した場合には、DWR はピギーバック・オプションを使います。
initApplicationScopeCreatorsAtStartup はアプリケーション・スコープを持つすべてのオブジェクトを DWR の起動時に初期化します。
ここで、クライアント・サイドでは 2 つのスクリプト・ファイルをインポートする必要があります (つまり DWR のクライアント・サイド・エンジンと、公開される com.dev.trade.service.StockService オブジェクトに対して DWR が作成する JavaScript プロキシー)。リスト 6 は、このインポートのためのコードを示しています。
リスト 6: クライアントにアクセスする
<script type="text/javascript" src="dwr/engine.js"></script>
<script type='text/javascript'
src='/brokerage/dwr/interface/StockService.js'></script>
|
また、アクティブな Reverse Ajax を使用するように DWR のクライアント・サイド・エンジンを構成する必要があり、そのためには dwr.engine.setActiveReverseAjax(true) を呼び出します。
StockService の getStocks メソッドを呼び出し、表示される Dojo のグリッド・ウィジェットに (StockService.getStocks({callback:getModel,async:false}) を使って) 初期データを追加します。getModel パラメーターは、この呼び出しの結果をメソッド・パラメーターとして渡されて呼び出される JavaScript メソッドを指します。
リスト 7 に示すメソッドは、表示される dojo grid に対するモデルを返します。
リスト 7: Web Remoting
function getModel(stocks) {
var data = new Array();
for ( var i = 0; i < stocks.length; i++) {
var subData = new Array();
subData[0] = stocks[i].symbol;
subData[1] = stocks[i].companyName
subData[2] = stocks[i].price
subData[3] = stocks[i].eps
data[i] = subData;
}
updatedModel = new dojox.grid.data.Table(null, data);
return updatedModel;
}
|
Dojo を構成する
Community Edition には Dojo JavaScript ライブラリーがバンドルされています。Community Edition はいくつかの管理コンソール・ポートレット用に Dojo を使用します。Dojo はコンテキスト・ルート /dojo で使用できる Web アプリケーションとしてバンドルされています。HTML によるクライアント・ページで Dojo を使うためには、リスト 8 に示すスクリプトを使って dojo.js スクリプトをインポートする必要があります。
リスト 8: dojo.js をインポートする
<script type="text/javascript" src="/dojo/dojo/dojo.js"
djConfig="isDebug:false, parseOnLoad: true"></script>
|
Dojo スクリプトがインポートされると、残りの依存関係は dojo.require 呼び出しを使ってインポートすることができます。これから作成する dojox.Grid ウィジェットにはモデルと構造が必要です。モデルは表示されるデータを表し、構造はそうしたデータの表示方法を表します。モデルは実際には配列の配列です。リスト 9 はこの構造の定義を示しています。
リスト 9: 構造の定義
var view1 = {
noscroll :false,
cells : [ [ {
name :'Symbol',
width :'auto'
}, {
name :'Company Name',
width :'auto'
}, {
name :'Price',
width :'auto'
}, {
name :'Earnings Per Share',
width :'auto'
} ] ]
};
var gridLayout = [ {
type :'dojox.GridRowView',
width :'20px'
}, view1 ];
<div class="tundra" id="stock_grid" dojoType="dojox.Grid" model="updatedModel"
structure="gridLayout" elasticView="1" defaultHeight="37em"></div>
|
このリストを見ると、id="stock_grid" の div によって実際のグリッドがカプセル化されていることがわかります。この構造は、view1 を含む配列である gridLayout オブジェクトによって定義されます。
view1 変数も配列であり、すべての列をグリッドの中にどう表示するかの詳細を記述する cells プロパティーを含んでいます。
Web アプリケーションをビルドし、デプロイする
アプリケーションをビルドするためには Apache Maven2 が必要です。Apache Maven2 をインストールし、以下のステップに従ってアプリケーションをビルドします。
ticker.zip アーカイブを解凍します。すると ticker というディレクトリーが作成されます。
- コマンド・シェルを開き、カレント・ディレクトリーを
ticker に変更します。
mvn install と入力して実行します。これによってアプリケーションがビルドされ、ticker の下にある target ディレクトリーの中に出力 WAR ファイルが置かれます。この WAR ファイルの名前は ticker.war です。
- ブラウザーで Community Edition のコンソールを開き、Deploy New ポートレットまでナビゲートします。
- 生成された
ticker.war へのパスを Archive テキスト・ボックスに入力し、Install をクリックします。
Web アプリケーションをテストする
- Web ブラウザーで http://localhost:8080/ticker を開くと株価情報表示アプリケーションが表示されます (図 4)。
図 4. ブラウザーで実行される株価情報表示アプリケーション
- Web ブラウザーで http://localhost:8080/ticker/comet を開くと、Comet サーブレットの出力が表示されます (図 5)。
図 5. Comet サーブレットの出力
まとめ
この記事では、Community Edition に組み込まれている Tomcat インスタンスの NIO コネクターを Community Edition の管理コンソールを介して作成し、構成しました。また、サンプル・アプリケーションとして、DWR の Reverse Ajax 機能を使用することで NIO コネクターに組み込みの Comet サポートを介してサーバーからブラウザーにデータをプッシュする、Java EE による Web アプリケーションを作成しました。最後に、Tomcat の Comet 機能を公開するサーブレットを作成するために Tomcat の CometProcessor インターフェースを実装する方法を説明しました。
謝辞
この記事のレビューをしてくださった Phani Madgula と Ashish Jain 両氏に感謝いたします。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Sample source code | ticker.zip | 8KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Manu T. George はインドのバンガロールにある IBM India Software Labs の Staff Software Engineer です。彼は Apache Geronimo プロジェクトと Apache OpenEJB プロジェクトのコミッターであり、IBM WebSphere Application Server Community Edition Level 3 Support Team の一員でもあります。彼は 2001年に College of Engineering Trivandrum で応用電子工学の学位を取得しています。 |
 | 
|  | Vamsavardhana Reddy Chillakuru (別名は Vamsi) はインドのバンガロールにある IBM India Software Labs の Advisory Software Engineer です。彼は Apache Geronimo プロジェクトと Apache Tuscany プロジェクトのコミッターであり、Apache Geronimo Project Management Committee のメンバーであり、そして IBM WebSphere Application Server Community Edition Level 3 Support Team の一員でもあります。彼はインドのカルカッタにある Indian Statistical Institute で 1994年 と 1996年にそれぞれ統計学の (優等) 学位と修士号を取得しています。 |
記事の評価
|