オープン・ソースの Ajax フレームワークと商用ツールキットは、下位レベルの JavaScript と DOM (Document ObjectModel) 操作の複雑さと重荷を隠すことで、Ajax 開発をもっと取りかかりやすいものにしようとしています。多くの場合、そうしたフレームワークやツールキットは開発時間を削減するためには役立ちますが、それらを使って高度な機能を実現しようとすると、簡単には身につけられないような、高度なスキルとJavaScript の知識を要求されることが普通です。しかし Google は、Ajax アプリケーション開発をまったく新しいレベルに高める革新的な技術をもたらしました。GWTは API やツールを包括的に集めたものであり、GWT を利用すれば動的な Web アプリケーションを Java コードで作成することができます。GWTはこれを、コード生成によって実現しています。GWT コンパイラーは、さまざまなブラウザーと互換性を持つ JavaScript を、クライアント・サイドのJava コードから生成します。
しかし残念なことに、Google が提供する標準の UI コントロール (ウィジェット) の機能は限定されており、たいていの場合、最近のエンタープライズ・クラスのアプリケーションには不十分です。一連のカスタム・プロパティーを使って基本的なGWT コントロールを拡張することはできますが、その多くはかなりの開発努力が要求されます。幸いなことに、もっと容易な方法があります。この記事ではサンプル・アプリケーション(「ダウンロード」セクションを参照) を利用して、一般的な ActiveWidgets JavaScript グリッド・コンポーネントを GWT アプリケーションと統合し、比較的単純なコーディングで高度な機能を実現するための方法を説明します。
JavaScript による Web UI コントロールは、HTML をブラウザー内部で動的に生成することでサーバーへの往復時間をなくすように設計されています。サーバーは静的なHTML を送信する代わりに、動的コンポーネントのためのデータ構造と JavaScript コードをページ内容に追加します。ブラウザーは、ページをロードする際にそのスクリプトを実行してアクティブなコンポーネントを作成し、それらを構成し、そして各コンポーネントが生成するHTML ストリングをページ上の適切な場所に挿入します。この時点以降は、JavaScript コンポーネントのコードはページ上の HTML フラグメントに結びつけられます。このコンポーネントはユーザーとの対話動作を処理し、データの変化に応じてHTML を更新し、またそのコンポーネントの内容や動作、視覚スタイルを操作するための API を提供します。
Java コードで開発され JavaScript にコンパイルされる GWT コントロールも、それとまったく同じように動作します。そのため GWTコントロールは、商用のコントロールや JavaScript ライブラリーと完全な互換性があります。JavaScript ライブラリーは成熟しており、さまざまなブラウザーで動作する高度な機能(インテリジェントなスクロールや列のサイズ変更、ソート、動的ロード、大規模データ・セットに対するページングなど) を完璧に備えて提供しています。GWTの持つオープンなアーキテクチャーを利用すれば、商用のコントロールや JavaScript のレガシー・ライブラリーを新しいアプリケーションと統合することができ、既に作成されているおなじみのものを再度作成する無駄をせずにエンタープライズ・レベルの課題に集中することができます。
この記事のサンプル GWT アプリケーション (「ダウンロード」を参照) は、GWT の便利な機能、自動リソース・インジェクション (automatic resource injection) を使っています。プロジェクト・モジュールには外部のJavaScript ファイルと CSS (Cascading Style Sheets) ファイルへの参照が含まれているため、このモジュール自体がロードされると、そうしたファイルも自動的にロードされます。リスト1 は、これを実現するための方法で、サンプル・アプリケーションのモジュールを定義する XML ファイルの中の宣言を示しています。
リスト 1. GWT のモジュール定義
<module>
<inherits name='com.google.gwt.user.User'/>
<entry-point class='com.mycompany.project.client.GridDemo'/>
<source path="client"/>
<stylesheet src="runtime/styles/xp/aw.css"/>
<script src="runtime/lib/aw.js">
<![CDATA[
if ($wnd.AW.Grid.Control.create)
return true;
else
return false;
]]>
</script>
</module>
|
JavaScript ファイルとスタイルシートをインジェクションによる方法は、外部ファイルを自動的に GWT モジュールと関連付けるための便利な方法です。インジェクトされたすべてのリソースを、GWTWeb サーバーのルートを定義する com\mycompany\project\public ディレクトリーに置く必要があります。
AW.Grid.Control.create はスクリプト ready 関数であり、グリッド・コントロール・スクリプトが初期化されることがわかっている場合に true を返します。ready 関数の目的は、そのスクリプトが完全にロードされたことを明確に判断することです。それによって GWT コードはそのスクリプトを使用でき、また参照される識別子が用意できていることを確認できます。リスト1 の例では、AW.Grid.Control.create 関数が存在していることで、スクリプトが用意できていることがわかります。この関数は GWT コントロールのライブラリーの一部であり、また外部リソースとして、この関数には$wnd という接頭辞を付ける必要があります。
JavaScript Native Interface が救いに
JavaScript グリッド・コントロールの統合そのものは、GWT の JSNI (JavaScript Native Interface)の実装を基にしています。JSNI は、Java ソース・コードに JavaScript コードを含められる強力な機能です。リスト 2 は、JavaScriptグリッド・コントロールが JSNI によってインスタンス化され、初期化される様子を示しています。
リスト 2. グリッド・コントロールの初期化 (一部)
native JavaScriptObject init(JavaScriptObject myColumns,JavaScriptObject myData)/*-{
try{
$wnd.mygrid = new $wnd.AW.UI.Grid;
$wnd.mygrid.setSize(750, 350);
// provide cells and headers text
$wnd.mygrid.setCellText(myData);
$wnd.mygrid.setHeaderText(myColumns);
// set number of rows/columns
$wnd.mygrid.setRowCount(myData.length);
$wnd.mygrid.setColumnCount(myColumns.length);
...
$doc.getElementById('mygrid').innerHTML = $wnd.mygrid;
return $wnd.mygrid;
}
catch(e){
$wnd.alert(e.description);
}
return null;
}-*/;
|
JSNI は、インライン・アセンブリー・コードの Web 版と考えることができます。JSNI を使うことで、
- Java メソッドを直接 JavaScript で実装することができます。
- 既存の JavaScript の前後をタイプセーフな Java メソッド・シグニチャーでラップすることができます。
- JavaScript から Java コードを呼ぶことができ、その逆も可能です。
- Java/JavaScript の境界にまたがって例外をスローすることができます。
- JavaScript から Java フィールドを読み書きすることができます。
- ホスト・モードを使うことで、Java ソースと JavaScript の両方をデバッグすることができます (Java ソースは Java デバッガーで、JavaScriptはスクリプト・デバッガーでデバッグします)。
GWT の開発用ドキュメンテーションでは、JSNI を慎重に使うように警告されています。その理由は、ブラウザー間の移植性が保証されておらず、コンパイルされるコードの最適化が限定的であるためです。商用コントロールの場合には、複数のブラウザー・タイプとデプロイメント・プラットフォームをサポートするというベンダーの確約に委ねることができます。
JSNI からブラウザーのウィンドウと文書オブジェクトにアクセスする際には、それぞれを $wnd と $doc として参照する必要があります。コンパイルされたスクリプトはネストされたフレームで実行され、$wnd と $doc は自動的に初期化されてホスト・ページのウィンドウと文書を正しく参照します。
サンプル・コードは JSNI を使った便利な方法を示しています。ここでは 1 次元と 2 次元の Java ストリング配列を JavaScript配列に変換し、そしてグリッドにデータを追加しています (リスト 3)。
リスト 3. Java から JavaScript への配列変換
public static JavaScriptObject arrayConvert(String[] array) {
JavaScriptObject result = newJSArray(array.length);
for (int i = 0; i<array.length; i++) {
arraySet(result, i, array[i]);
}
return result;
}
private static native JavaScriptObject newJSArray(int length) /*-{
return new Array(length);
}-*/;
public static native int arrayLength(JavaScriptObject array) /*-{
return array.length;
}-*/;
public static native String arrayGetObject(JavaScriptObject array, int index) /*-{
return array[index];
}-*/;
public static native void arraySet(JavaScriptObject array,int index,String value) /*-{
array[index] = value;
}-*/;
|
JavaScript で定義されたイベント・ハンドラーは、Java コードで実装されるメソッドと関連付けられます。リスト 4 は、グリッド・コントロールのonRowClicked イベントが onRowSelect という Java 関数を呼び出す様子を示しています。
リスト 4. JavaScript から Java 関数を呼び出す
public void onRowSelect(String index){
GWT.log("Row #" + index + "selected", null);
}
native JavaScriptObject init(JavaScriptObject myColumns, JavaScriptObject myData)/*-{
var widget = this;
try{
...
// set click action handler
$wnd.mygrid.onRowClicked = function(event, index){
widget.@com.mycompany.project.client.GwtGrid::onRowSelect(Ljava/lang/String;)(index);
};
...
}
catch(e){
$wnd.alert(e.description);
}
}-*/;
|
JavaScript で Java メソッドを参照するためには、次のような表記を使う必要があります。instance-expr.@class-name::method-name(param-signature)(arguments)
-
instance-expr.は、インスタンス・メソッドを呼び出す際には必要であり、静的メソッドを呼び出す際には存在してはなりません。 -
class-nameは、メソッドが宣言されているクラス (あるいはそのサブクラス) の完全修飾名です。 -
param-signatureは、ここで指定される内部 Java メソッド・シグニチャーですが、その後にメソッド戻り型のシグニチャーはありません (オーバーロードを選択するためには必要ないためです)。 -
argumentsは、呼び出されたメソッドに渡される実際の引数リストです。
リスト 2を見るとわかるように、この実装の Java クラスの init メソッドは、グリッド・インスタンスを表す JavaScriptObject を返します。このオブジェクトはクラス属性として保存することができ、また、後でこれをコードの中で参照して JavaScript コントロールAPI メソッドを呼び出すことができます。リスト 5 は、Delete ボタンのクリック・イベントが Java コードで処理される様子を示しています。
リスト 5. JavaScript メソッドを呼び出す
protected JavaScriptObject grid = null;
...
public void onLoad(){
if(grid == null){
grid = init(...);
}
}
public void onDeleteButtonClick(){
delete(grid, getCurrentRow(grid));
}
public native void delete(JavaScriptObject obj, int index) /*-{
try{
obj.deleteRow(index);
}
catch(e){
$wnd.alert(e.description);
}
}-*/;
public native int getCurrentRow(JavaScriptObject obj) /*-{
try{
return obj.getCurrentRow();
}
catch(e){
$wnd.alert(e.description);
}
return -1;
}-*/;
|
リスト 5 で、Java イベント・ハンドラーは JavaScript コントロール API メソッドを呼び出します。これは、このコントロール・インスタンスとの対話機能を示しています。同様のJava ラッパーをすべての JavaScript コントロール API メソッドに対して定義し、それらを GWT の世界で利用できるようにすることができます。
このデモ・アプリケーションを利用するためには、システムに GWT SDK をインストールする必要があります。サンプル・コードを実行するためには、ダウンロードしたアーカイブ(ソースと、コンパイルされたスクリプトが入っています) を、GWT SDK ディストリビューションのディレクトリーの samples フォルダーの中に単純に解凍します。GWTのサンプルを実行できれば、次のコマンドを使ってサンプル・アプリケーションを起動することができます (Windows プラットフォームの場合)。
your_local_path\gwt-windows-1.3.3\samples\GridDemo\GridDemo-shell.cmd |
このサンプル GWT アプリケーションは、商用の JavaScript グリッド・コンポーネントを統合した様子を示しています。このコンポーネントはActiveWidgets 社によって開発されたもので、同社の Web サイトから評価用にダウンロードが提供されています (「参考文献」を参照)。
図 1. サンプル GWT アプリケーションでの高度なグリッド・コントロール
このデモ・アプリケーションのアーカイブには、必要なライブラリーがすべて含まれています (GWT を除きます)。私はこのアプリーションを、主なGWT リリースすべて (バージョン 1.3 を含みます) を使ってテストしました。
この記事では、レガシーな JavaScript グリッド・コントロールを GWT アプリケーションと統合する方法の一例を紹介しました。同じ方法を、市場で入手できる他の高度なコントロールにも適用することができます。また、商用のJavaScript コントロールに代わるものとして、Yahoo! User Interface Library を調べてみてください (「参考文献」を参照)。このライブラリーはオープン・ソースの JavaScript ユーティリティーとコントロールを集めたもので、DOM スクリプトや DHML、そしてAjax などの手法を使った対話性に富んだ Web アプリケーションを構築する際に活用することができます。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Sample GWT application | j-gwtcontrols.zip | 503KB | HTTP |
学ぶために
-
「Web 2.0: 次世代ソフトウェアのデザインパターンとビジネスモデル」(Tim O'Reilly 著、O'Reilly Network、2005年9月) は、次世代ソフトウェアの設計パターンとビジネス・モデルを解説しています。
-
「Java開発者のためのAjax: Google Web Toolkitを探る」(Philip McCarthy 著、developerWorks、2006年6月) は、GWT を包括的、実際的に紹介しています。
-
Ajax resource centerは Ajax プログラミング・モデルに関する情報を入手できるワンストップ・ショップとして、記事やチュートリアル、ディスカッション・フォーラム、ブログ、ウィキ、イベント、ニュースなどが豊富に用意されています。
- developerWorks のJava technology ゾーンには Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。
製品や技術を入手するために
- Google Web ToolkitSDKをダウンロードしてください。
-
ActiveWidgetsから ActiveWidgets JavaScript コンポーネント・ライブラリーの評価版をダウンロードしてください。
-
Yahoo! User Interface Libraryはオープン・ソースの JavaScript ユーティリティーとコントロールです。
議論するために
- GWT の開発者達によって更新されているディスカッション・グループ、GWT Developer Forumに参加してください。
-
developerWorks blogs から developerWorks のコミュニティーに加わってください。