目次


Google Web Toolkit と Eclipse Galileo を使ったハイパフォーマンスの Web 開発

Comments

前提条件

この記事では GWT のいくつかの機能を取り上げ、それらをどのように使うことでハイパフォーマンスの Web アプリケーションを実現できるのかを検証します。この記事は GWT の紹介を目的とするものではないので、読者が既に GWT を使ったことがある前提のもとで話を進めます。また、Java 技術を理解していることに加え、JavaScript、CSS、HTML についても十分理解しているものとします。GWT としては、最新版を使用することをお奨めします (この記事では V1.7 を使用しました)。この記事では、Google Plugin for Eclipse も使用しますが、そのバージョンとしては Google Plugin の V1.1 をEclipse V3.5 (Galileo) と共に使用しました。また、Mozilla Firefox の Firebug プラグインも使用しますが、バージョンとしては Firebug の V1.4.2 を Firefox V3.5.2 で使用しています (「参考文献」)。

より高速な JavaScript

GWT は Java を JavaScript にコンパイルできることでよく知られています。この機能を利用すると、Java を使用して動的な Web アプリケーションの開発をすることができます。GWT では、すべてのコードは JavaScript にコンパイルされ、また GWT のパフォーマンス機能の多くはブラウザーで JavaScript を高速に実行できるように設計されています。GWT のそうした機能には、例えば特定のブラウザー用に最適化する機能や Ajax を高速化するための機能などがあります。これらの機能の大半は、GWT のコンパイラーを使用して Java を JavaScript に変換することから始まります。そこで、GWT のパフォーマンス関連機能を調べるために、まずはコンパイラーを調べることにします。

コンパイラーの最適化

GWT のコンパイラーは Java コードを変換し、ブラウザーで実行する JavaScript にします。しかし、このコンパイラーは単に Java 技術を JavaScript に変換するだけではありません。このコンパイラーは、より高速にコードを実行できるように、数々の最適化を行います。ところが、これらの最適化によって実際に何が行われているかを理解するのは容易なことではありません。GWT から出力される JavaScript は難読化されており、非常に理解しにくいからです。そこでまず、人間にも読みやすい JavaScript を生成するように GWT コンパイラーに指示し、コンパイラーが実際にどのような最適化を行っているのかをよく理解する必要があります。

GWT のコンパイラーには 3 つの動作モードがあります。デフォルトのモードは「Obfuscated」であり、このモードでは GWT のコンパイラーは難読化された JavaScript を出力します。この JavaScript は読みにくいだけではなく、圧縮されています。そのため JavaScript のサイズが小さくなり、インターネットから高速にロードすることができます。また、サイズが小さいため、ブラウザーは JavaScript をより高速に解析することができます。

読者の中には、圧縮された JavaScript をネットワーク上に送信しても、ほとんど差はないと思う人がいるかもしれません。最近はどの Web ブラウザーでも gzip 圧縮がサポートされており、ほとんどの Web サーバーは gzip 圧縮を使って JavaScript を送信するからです。しかし GWT のコンパイラーは単に JavaScript を圧縮するだけではなく、既に gzip で圧縮されている想定のもとに圧縮を行うのです。つまり、「Obfuscated」モードに設定された GWT から生成される JavaScript は、既に圧縮されているにもかかわらず、さらに gzip で圧縮することができるのです。従って、Web サーバーが gzip を使用していない場合には、GWT の「Obfuscated」モードによって明らかに大幅な高速化を実現することができます。ところが、Web サーバーが gzip を使用している場合であっても、やはり GWT の「Obfuscated」モードのおかげで大幅な高速化を実現できるのです。

つまり本番コードの場合には、「Obfuscated」モードで JavaScript を出力するように GWT コンパイラーを設定する必要があることは明らかです。しかしそうすると当然、出力された JavaScript を読むことは、ほとんど不可能になります。この例として、「Obfuscated」モードに設定した GWT からの JavaScript の出力をリスト 1 に示します。

リスト 1. 「Obfuscated」モードによる JavaScript 出力
function qH(){return np}
function mH(){}
_=mH.prototype=new mu;_.gC=qH;_.tI=0;function
uH(){uH=ZH;sH={};tH=[];sH[LM]=[Is,Hs,Js];sH[JM]=[rt,qt,st];Xv(tH,yn,LM);Xv(tH,To,JM)}
var sH,tH;function AH(a){a.b=oH(new mH);return a}
function BH(a){var b,c,d,e,f,g,h,i,j,k;g=ox(new cx,MM);f=OA(new FA);j=XH(new
 VH,NM,OM);KA(f,j.b+sJ+j.c);pw(g.B,PM,true);Zw(gA(QM),f);Zw(gA(RM),g);f.B.focus()
;k=Jg(f.B,NJ).length;k>0&&JA(f,0,k);c=py(new my);Lf((tf(),c.b.B),SM);c.o=true;
b=ox(new cx,TM);b.B.id=UM;i=Py(new Ny);h=Ty(new My);d=UA(new RA);pw(d.B,VM,true);
VA(d,Uy(new My,WM));VA(d,i);VA(d,Uy(new My,XM));VA(d,h);d.b=(kz(),jz);VA(d,b);
Ax(c.j,d);Mx(c);vw(b,FH(new DH,c,g),(sh(),rh));e=KH(new IH,a,g,f,i,h,c,b);
vw(g,e,rh);vw(f,e,(hi(),gi))}
function CH(){return rp}
function xH(){}

幸いなことに、もっと人間が読みやすい JavaScript を作成するように、GWT コンパイラーを簡単に調整することができます。そのためには単に -style=PRETTY 引数をコンパイラーに渡します。これは Google Plugin for Eclipse を使用すると、さらに容易になります。GWT のコンパイル機能を起動すると、図 1 のダイアログが表示されます。

図 1. GWT コンパイラーのオプション
GWT コンパイラーのオプションを表示するスクリーン・ショット
GWT コンパイラーのオプションを表示するスクリーン・ショット

コンパイラーがどんな JavaScript を出力しているのかを調べるためには、図 1 の画面で「Output style」を「Pretty」に設定します。すると、出力されるコードはリスト 2 のようになります。

リスト 2. 「Pretty」に設定した場合に出力される JavaScript
var $wnd = parent;
var $doc = $wnd.document;
var $moduleName, $moduleBase;
var $strongName = '21B409FCD39529C5A9DB925F7D8D9A95';
var $stats = $wnd.__gwtStatsEvent ? function(a) {return 
   $wnd.__gwtStatsEvent(a);} : null;
$stats && $stats({moduleName:'gwtperf',subSystem:'startup',evtGroup:
'moduleStartup',millis:(new Date()).getTime(),type:'moduleEvalStart'});
var _;
function nullMethod(){
}

function equals(other){
  return this === (other == null?null:other);
}

function getClass_0(){
  return Ljava_lang_Object_2_classLit;
}

function hashCode_0(){
  return this.$H || (this.$H = ++sNextHashId);
}

function toString_0(){
  return (this.typeMarker$ == nullMethod || this.typeId$ ==
2?this.getClass$():Lcom_google_gwt_core_client_JavaScriptObject_2_classLit)
.typeName + '@' + toPowerOfTwoString(this.typeMarker$ == nullMethod || this.typeId$
== 2?this.hashCode$():this.$H || (this.$H = ++sNextHashId), 4);
}

function Object_0(){
}

_ = Object_0.prototype = {};
_.equals$ = equals;
_.getClass$ = getClass_0;
_.hashCode$ = hashCode_0;
_.toString$ = toString_0;
_.toString = function(){
  return this.toString$();
}
;

これでも JavaScript は非常に最適化されていますが、前に比べるとはるかに理解しやすくなっています。もちろん、こうすることによって、作成される JavaScript のサイズに大きな差が生じます。これは Firefox 用の Firebug プラグインを使用することで確認することができます (図 2)。

図 2. 「Obfuscation」モードと「Pretty」モードでの JavaScript ファイルのサイズを比較する
「Obfuscation」モードと「Pretty」モードでの JavaScript ファイルのサイズを比較したスクリーン・ショット
「Obfuscation」モードと「Pretty」モードでの JavaScript ファイルのサイズを比較したスクリーン・ショット

図 2 は、同じ GWT アプリケーション (Google Plugin で作成されたスターター・プロジェクト) を JavaScript にコンパイルする際に、「Obfuscated」モードに設定した場合 (上) と、「Pretty」モードに設定した場合 (下) を示しています。この図を見るとわかるように、「Obfuscated」モードから「Pretty」モードに設定を変更すると、JavaScript のサイズは 58 KB から 146 KB になります。

今度は少しコードを調べ、GWT コンパイラーがどのような方法でコードを最適化しているかを見てみましょう。GWT の基本となる考え方の 1 つに、コードを作成する際には、ソフトウェア・エンジニアリングのベスト・プラクティスを使用するというものがあります。この考えに基づいてコードの適切な抽象化を行うことで、堅牢かつ保守が容易なコードを作成することができます。その一方、GWT によるコンパイルの結果、非常に高速に実行することが可能なコードが生成されます。一例として、ユーザーをモデリングする簡単なクラスを見てみましょう (リスト 3)。

リスト 3. Person クラス
public class Person {
    final String firstName;
    final String lastName;
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String getName(){
        return firstName + " " + lastName;
    }
}

このコードを利用して、GWT のスターター・プロジェクトのコード (リスト 4) を変更します。

リスト 4. GWT のスターター・プロジェクトのオリジナルのコード
final TextBox nameField = new TextBox();
nameField.setText("GWT User");

上記でハードコーディングされているストリングの代わりに、Person オブジェクトを使うことにします。この変更を行った後のコードをリスト 5 に示します。

リスト 5. Person を使うように変更する
final TextBox nameField = new TextBox();
final Person user = new Person("GWT", "User");
nameField.setText(user.getName());

これは単純な変更ですが、こうしておくとアプリケーションを抽象化する上で有効であることは容易に想像することができます。しかし、パフォーマンスの点では問題があるかもしれません。オブジェクトの作成とメソッドのディスパッチには (潜在的な) オーバーヘッドがあります。この問題に対して GWT コンパイラーがどのように役立つかを調べてみましょう。リスト 6 はオリジナルのコードをコンパイルしたものです。

リスト 6. オリジナルのコードを JavaScript にコンパイルする
nameField = $TextBox(new TextBox());
nameField.element['value'] = 'GWT User' != null?'GWT User':'';

今度は、リスト 5 のコードを JavaScript にコンパイルした場合にどうなるかを見てみましょう。コンパイルによって生成されたコードをリスト 7 に示します。

リスト 7. 変更したコードを JavaScript にコンパイルする
  user = $Person(new Person(), 'GWT', 'User');
  $setText_1(nameField, user.firstName + ' ' + user.lastName);

1 行目のコードは Person クラスの JavaScript コンストラクターを呼び出しています。これは Java コードを直接変換したものです。2 行目のコードは少し変更されています。Person インスタンスの getName() メソッドを呼び出す代わりに、このメソッドをインライン化しています。つまり、メソッドの呼び出しがそのメソッドの本体に置き換えられているのです。これはコンパイラーによって行われる一般的な最適化であり (従来から、C/C++ コンパイラーや Java コンパイラーなどのコンパイラーで行われています)、GWT コンパイラーもこの最適化を最大限に利用し、高速な JavaScript を生成しています。

オブジェクト指向の開発では、実装が複数考えられる共通のインターフェースを抽象化し、この共通インターフェースを使用するコードを作成することで、コードの再利用性を高めるということが一般的に行われています。

これもエンジニアリングによる抽象化が有効な例の 1 つですが、余分な参照やメソッドのディスパッチが行われることで、パフォーマンスが低下する可能性があります。では、この問題に対して GWT コンパイラーがどのように役立つかを調べてみましょう。Eclipse のリファクタリング・ツールを使うと、リスト 3 の Person クラスから容易にインターフェースを抽出することができます。このインターフェースを Thing と呼ぶことにし、そのコードをリスト 8 に示します。

リスト 8. Thing インターフェースを使ってリファクタリングする
public interface Thing {
    String getName();
}
public class Person implements Thing {
    final String firstName;
    final String lastName;
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public String getName(){
        return firstName + " " + lastName;
    }
}

今度は、Person を使用するクライアント・コードを、Thing インターフェースを使うように変更する必要があります。この変更を行ったコードをリスト 9 に示します。

リスト 9. リファクタリングされたクライアント・コード
Thing user = new Person("GWT", "User");
nameField.setText(user.getName());

ではコンパイルすることによって生成されたコードはどうなのでしょう。パフォーマンスの低下はあるのでしょうか。リスト 10 を見てください。

リスト 10. リファクタリングされたコードを再コンパイルして生成されたコード
user = $Person(new Person(), 'GWT', 'User');
$setText_1(nameField, user.firstName + ' ' + user.lastName);

これを見るとわかるように、まったく何も変更はありません。もし、この Thing インターフェースの実装が 2 つあり、両方とも使用されるとしたらどうでしょう。そのコードがリスト 11 です。

リスト 11. Thing を複数実装する
public class Company implements Thing {
    private final String name;
    public Company(String name){
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}
// client code
final TextBox nameField = new TextBox();
Thing user = new Person("GWT", "User");
Thing userCompany = new Company("ACME");
nameField.setText(userCompany.getName() + " " + user.getName());

リスト 12 は GWT コンパイラーから出力された JavaScript です。

リスト 12. 複数の実装をコンパイルする
user = $Person(new Person(), 'GWT', 'User');
userCompany = $Company(new Company(), 'ACME');
$setText_2(nameField, userCompany.name_0 + ' ' + (user.firstName + ' ' + user.lastName));

これを見るとわかるように、やはりコンパイラーによってインターフェースが削除され、getName() メソッドの各実装がインライン化されています。これでもまだかなり最適化されたコードです。ところが、最適化が行われないようにすることもできるのです (リスト 13)。

リスト 13. コンパイラーを無効にする
private String mkString(Collection<Thing> things, char separator){
    StringBuilder sb = new StringBuilder();
    for (Thing thing : things){
        sb.append(thing.getName());
        sb.append(separator);
    }
    return sb.deleteCharAt(sb.length()).toString();
}
// client code
final TextBox nameField = new TextBox();
Thing user = new Person("GWT", "User");
Thing userCompany = new Company("ACME");
nameField.setText(mkString(Arrays.asList(user, userCompany), ' '));

リスト 13 では新しい抽象化が導入されており、ヘルパー関数が Thing オブジェクトのコレクションを引数に取って、各 Thing に対して getName() を呼び出した結果を連結しています。セパレーターも、この関数の引数として抽象化されています。ではコンパイルによって生成された JavaScript を見てみましょう (リスト 14)。

リスト 14. コンパイルによって生成された mkString のコード
function $mkString(things, separator){
  var sb, thing, thing$iterator;
  sb = $StringBuilder(new StringBuilder());
  for (thing$iterator = $AbstractList$IteratorImpl(new AbstractList$IteratorImpl(), 
things); thing$iterator.i < thing$iterator.this$0.size_0();) {
    thing = dynamicCast($next_1(thing$iterator), 16);
    $append_2(sb, thing.getName());
    sb.impl.string += String.fromCharCode(separator);
  }
  return $deleteCharAt(sb, sb.impl.string.length).impl.string;
}
// client code
user = $Person(new Person(), 'GWT', 'User');
userCompany = $Company(new Company(), 'ACME');
$setText_1(nameField, $mkString($Arrays$ArrayList(new Arrays$ArrayList(), 
initValues(_3Lorg_developerworks_gwt_client_Thing_2_classLit, 0, 16, 
  [user, userCompany])), 32));

リスト 14 のコードの複雑さは Java のソース・コードと同程度です。ループの中で、dynamicCast という関数が呼び出されていることに注意してください。この関数は、渡されたオブジェクトを指定型のオブジェクトにキャストできるかどうかをチェックする JavaScript で、ここではオブジェクトが Person なのか Company なのかをチェックします (この 2 つのオブジェクトのみが Thing を実装しているため)。インターフェースを実装するコードを導入し、そのインターフェースの実装を複数持つようにすることで、GWT コンパイラーによる最適化を減らすことができます。

ここまで説明してきた最適化はすべて、GWT コンパイラーによる言語レベルの最適化でした。GWT によって行われる最適化には、言語レベルで行われる最適化のほかに、特定のブラウザー用の最適化があります。このタイプの最適化は一般的に、遅延バインディングとして知られる概念に含められます。

遅延バインディング

Web 開発者は、Mosaic が世の中で唯一のブラウザーではなくなった時以来、さまざまな Web ブラウザー間の違いに苦しんできました。その苦しみを少しでも軽減してくれるものに、JavaScript フレームワークがあります。巷には数多くの JavaScript フレームワークがありますが、その大きな魅力の 1 つは、ブラウザー間の違いを吸収してくれることです。ブラウザー間の違いを吸収するために使われる一般的な方法は 2 つあります。第 1 の方法は、さまざまなブラウザーに移植可能なコードを作成する方法です。これは最低限の共通機能を提供する手法であり、最も良い場合でも最適なコードを提供するところまではいかず、ほとんどの場合は最適とはほど遠いコードになります。もう 1 つの方法は、ブラウザーを判別し、各ブラウザー用に最適化されたコードを使う方法です。この方法では大量のスパゲティー・コードが生成され、その結果、決して実行されることのない大量のコードがブラウザーに渡されることになります。

GWT による遅延バインディングのアーキテクチャーを利用すると、GWT はさまざまなブラウザー用に複数のバージョンの JavaScript をコンパイルすることができます。最初に簡単な JavaScript がブラウザーにダウンロードされ、ブラウザーが判別された後、そのブラウザー用に最適化された JavaScript がダウンロードされます。図 2 を見ると、nocache.js ファイルがダウンロードされているのがわかると思います。このファイルはブラウザーを判別するコードであり、この場合のサイズは 4 KB です。nocache.js では、デフォルトで 4 種類 (複数のバージョンを含む) のブラウザーがチェックされます (Opera、Safari、Gecko (Firefox V2 またはそれ以前)、Gecko V1.8 (Firefox V3 またはそれ以降)、Internet Explorer V6、Internet Explorer V8)。

ブラウザーによって大幅に異なる API の典型的な例として、要素の innerText プロパティーの設定があります。この設定はスターター・プロジェクトの中で、コールバック・ハンドラーがサーバーに対して リモート・プロシージャー・コール (RPC) を実行する場合に使われています。この Java コードは非常に単純です (リスト 15)。

リスト 15. GWT でテキストを設定する
public void onSuccess(String result) {
    dialogBox.setText("Remote Procedure Call");
    serverResponseLabel.removeStyleName("serverResponseLabelError");
    serverResponseLabel.setHTML(result);
    dialogBox.center();
    closeButton.setFocus(true);
}

では、このコードから GWT コンパイラーがさまざまなブラウザーに対して何を出力するのかを見てみましょう。各ブラウザーに対してどんなファイルがコンパイルされているのかを調べるために、nocache.js ファイルの中を調べ、リスト 16 のコードに似たコード・セクションを探します。

リスト 16. GWT のブラウザー判別コード
if (!strongName) {
  try {
    unflattenKeylistIntoAnswers(['opera'], 
'D1B884746B9C511E12378A55F9FD97E2.cache.html');
    unflattenKeylistIntoAnswers(['safari'], 
'12DC532DA52018F17FA7F84F7137102A.cache.html');
    unflattenKeylistIntoAnswers(['gecko1_8'], 
'0986E60F243CC620FA7138AB06F221EB.cache.html');
    unflattenKeylistIntoAnswers(['gecko'], 
'CF1F7CBAF43D18B03F82260D99CB1803.cache.html');
    unflattenKeylistIntoAnswers(['ie8'], 
'1EE88964C0A866A7F2887C02F69F64D3.cache.html');
    unflattenKeylistIntoAnswers(['ie6'], 
'5395DF4A8135D37430AAE1347158CE76.cache.html');
    strongName = answers[computePropValue('user.agent')];
  }
  catch (e) {
    return;
  }
}

今度は、それぞれのキー ('opera'、'safari' など) を、生成されたファイルと突き合わせます。この方法を使うと、リスト 15 の onSuccess メソッドを Internet Explorer V6 用にコンパイルしたバージョンが見つかります (リスト 17)。

リスト 17. Internet Explorer V6 用にコンパイルしたバージョン
function $onSuccess(this$static, result){
  ($clinit_11() , this$static.val$dialogBox.caption.element)
.innerText = 'Remote Procedure Call';
  setStyleName(this$static.val$serverResponseLabel.element, 
'serverResponseLabelError', false);
  this$static.val$serverResponseLabel.element.innerHTML = result || '';
  $center(this$static.val$dialogBox);
  $setFocus(this$static.val$closeButton, true);
}

Internet Explorer V6 に対して、GWT は Internet Explorer V6 用に最適な API を使用します (つまり要素の innerText プロパティーを使います)。ではこれを、Gecko V1.8 またはそれ以降用にコンパイルしたバージョンと比べてみましょう (リスト 18)。

リスト 18. Firefox V3 またはそれ以降用にコンパイルしたバージョン
function $onSuccess(this$static, result){
  ($clinit_11() , this$static.val$dialogBox.caption.element)
.textContent = 'Remote Procedure Call';
  setStyleName(this$static.val$serverResponseLabel.element, 
'serverResponseLabelError', false);
  this$static.val$serverResponseLabel.element.innerHTML = result || '';
  $center(this$static.val$dialogBox);
  $setFocus(this$static.val$closeButton, true);
}

新しいバージョンの Firefox に対しては、GWT は Java コードを、要素の textContent プロパティーを使用する JavaScript にコンパイルします。これは単純な例ですが、アプリケーション・コードの中で innerText のようなブラウザー独自のプロパティーが何度も使われているケースは容易に想像できるはずです。

GWT を使った開発を頻繁に行っていると、すぐに遅延バインディングが行われていることに気付きます。しかし必ずしも理想的な理由で行われているわけではありません。上で説明したように、GWT はアプリケーションをコンパイルし、各ブラウザーに対応する異なるバージョンの JavaScript を生成します。この、ブラウザーごとに異なるバージョンの JavaScript を生成する方式は、GWT がコードをローカライズする場合も同じです。GWT ではデフォルトで、ブラウザーおよびそのバージョンを 6 種類と 1 つの言語をサポートしていますが、もし 1 つの言語ではなく 3 つの言語をサポートしなければならない場合には、18 種類の組み合わせとなり、18 種類のバージョンの JavaScript が必要です。そうなると、多くの場合はコンパイル時間が長くなり、生産性に大きく影響します。もちろん、この問題を克服するための 1 つの方法として、JavaScript へのコンパイルをまったく行わない、ホスト・モードで大半の作業を行う方法があります。この方法であれば、コードのデバッグははるかに容易です。しかしコンパイルが必要な場合には、大幅にコンパイル時間を減らす方法として、1 つのブラウザー (テスト用に使用する任意のブラウザー) のみを対象にコンパイルするよう GWT に指示する方法があります。そのために必要なことは、アプリケーションのモジュール構成用の XML ファイルを変更することだけです (リスト 19)。

リスト 19. 1 つのブラウザー専用に GWT を構成する
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN" 
"http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core
/src/gwt-module.dtd">
<module rename-to='gwtperf'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->
  <entry-point class='org.developerworks.gwt.client.GwtPerf'/>
  <set-property name="user.agent" value="gecko1_8"/>
</module>

ここで重要なことは、最後の行の 1 行前にある set-property タグです。ここでは単純に user.agent プロパティーを gecko1_8 に設定しています。すると GWT は、Firefox V3 またはそれ以降を対象にした JavaScript を生成するコンパイルのみを行います。ここまでで、GWT が適切な JavaScript を生成する数々の方法のいくつかを説明しました。こうして生成された JavaScript はユーザーのブラウザーで最も高速に実行されるように最適化されています。

高速な Ajax

Ajax は Web では一般的に使われるようになっており、どのような Web アプリケーションにも不可欠です。Ajax という用語が考え出された当時、Ajax の X は XML を意味していました。これは、ブラウザーとサーバーとの間で交換されるデータのフォーマットが XML であることを前提にしていたためです。しかし実際には、多くの Ajax アプリケーションは通信対象のサーバーから HTML を受信しています。こうしたことがよく行われている理由は、実装が最も容易なためです。ただしこの方法は、とても最適とは言えません。

一部の Ajax アプリケーション・サーバーは XML 形式でデータを返送します。これは HTML を使うよりも大幅な改善ですが、やはり最適とは言えません。XML はサイズの大きなフォーマットであり、また XML の構文解析を JavaScript を使って行うことは簡単ではありません。XML 形式でデータが送信されると、大量のデータがネットワーク上に送信されることになると同時に、大量の JavaScript を実行しなければならなくなり、ユーザーはその間待たなければなりません。そのため、多くの Web アプリケーションでは、JSON を使う方式に切り換えており、改善がなされています。

GWT には、クライアント・サイドの Ajax コンポーネントと、サーバー・サイドの Ajax コンポーネントの両方が用意されています。そのため、GWT が Ajax に関してどんなデータ・フォーマットを使用するのだろうと思う人がいるかもしれません。驚くには当たらないと思いますが、GWT では高度に最適化された独自のフォーマットを使用しています。では GWT のフォーマットを調べてみましょう。この場合も Firebug が役に立ちます (図 3)。

図 3. Firebug を使って Ajax のトラフィックをモニタリングする
Firebug を使って Ajax のトラフィックをモニタリングする様子を示すスクリーン・ショット
Firebug を使って Ajax のトラフィックをモニタリングする様子を示すスクリーン・ショット

Firebug は、サーバーに送信されるデータとサーバーから返されるデータを表示します。より詳細に示したものがリスト 20 です。

リスト 20. GWT のリクエスト・データとレスポンス・データ
Request: 
5|0|6|http://localhost:8080/gwtperf/|29F4EA1240F157649C12466F01F46F60|
org.developerworks.gwt.client.GreetingService|greetServer|java.lang.String|
IBM developerWorks|1|2|3|4|1|5|6|
Response:
//OK[1,["Hello, IBM developerWorks!<br><br>I am running Google App Engine
Development/1.2.2.<br><br>It looks like you are using:<br>Mozilla/
5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/
3.5.2"],0,5]

もちろん、このフォーマットを扱うために必要な Java コードは非常に単純です。GWT では設計時のプログラミング・モデルは単純ですが、実行時の実装は高度に最適化されています。

まとめ

この記事では、GWT が提供しているさまざまなパフォーマンス最適化機能について説明し、これらの機能を利用するために Google Plugin for Eclipse Galileo を活用する方法についても説明しました。GWT を利用することで、動的かつハイパフォーマンスの Web アプリケーションを容易に作成することができます。この GWT の特長は今後もさらに、より優れたものになっていくことでしょう。近くリリースされる GWT V2.0 には新しい機能がいくつか含まれており (コード分割やリソース・バンドルなど)、GWT を使って作成される Web アプリケーションのパフォーマンスがさらに向上します。GWT トランクのソース・コードをビルドしてみると、これらの機能をリリース前に試すことができます。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Web development
ArticleID=448390
ArticleTitle=Google Web Toolkit と Eclipse Galileo を使ったハイパフォーマンスの Web 開発
publish-date=10202009