Webアプリケーションの大きな弱点の1つは、"user experience" (ユーザー体験) がデスクトップ・アプリケーションに比べて見劣りすることが多いという点です。現在のほとんどのWebアプリケーションには、対話性が欠如しています。それは、URL要求への応答をブラウザーが受け取ると、それはそれで完結してしまい、ユーザーがハイパーリンクをクリックしたりフォームを送信したりしない限り、サーバーへの通信は何も行われないからです。もちろん、JavaScriptやDHTMLを使用する技法などにより、ブラウザーの外観をデスクトップ・アプリケーションの外観に近づけることができます。また、Flash、ActiveX、およびJavaアプレットも、同じ目的で利用できます。
しかし、それらの比較的新しいテクニックやテクノロジーを利用したとしても、Webサーバーからコンテンツを受け取った後は、依然としてクライアントが単独の状態に取り残されてしまうことがほとんどです。この記事で説明する技法は、ブラウザーとWebサーバーが舞台裏で通信できるソリューションを提供するものです。ブラウザーは、リモートJavaサーブレットのメソッドを呼び出すことにより、"user experience" をデスクトップ・アプリケーションに似せることができます。たとえば、ドロップダウン・リストの内容を関連する別のドロップダウン・リストの選択内容に応じて動的に変化させること (つまり、カテゴリー/サブカテゴリー) や、サーバーにメッセージをポーリングして画面を動的に更新することによりコンテンツを継続的に更新し続けることが可能になります。
Webブラウザーからのリモート・メソッド呼び出しを実現する方法としては、2つの方法が一般的です。それは、Microsoft Remote Scripting (MSRS)とBrent Ashley氏のJavaScript Remote Scripting (JSRS) です。どちらの方法も目標は同じで、リモート・メソッドを呼び出して、結果をブラウザーに返すことを目標にしています。どちらの方法も、元々は、MicrosoftのActive Server Pages内で定義されたリモート・メソッドと通信するように設計されました。この記事では、この2つの方法において、サーバー側のJavaサーブレットと通信する手法について説明します。それでは、まず、この2つの技法のアーキテクチャーについて詳しくみてみましょう。
MicrosoftのRemote Scriptingは、Visual InterDev開発環境の一部を成しています。Remote Scriptingは、次の3つの部分で構成されます。すなわち、非表示のJavaアプレット、クライアント側のJavaScript、およびActive Server Pagesで実行されているサーバー側のJavaScriptです。Javaアプレットは、サーバーとの通信を処理します。クライアント側のJavaScriptは、そのアプレットと通信します。サーバー側のJavaScriptは、要求されたパラメーターを受け取り、指定されたサーバー側のメソッドにそれを引き渡します。通信の手段は、HTTPのGET要求とそれに対する応答で、メソッド呼び出しの詳細情報はサーバー側のスクリプトに対する照会パラメーターとして送信されます。MicrosoftのRemote Scriptingについてはもっと説明するべき点がありますが、それはこの記事の守備範囲を越えています ( 参考文献 を参照)。
図1 に、サーバー側でサーブレットを使用しているMicrosoft Remote Scriptingのアーキテクチャーを示します。
Microsoft Remote Scriptingを使用してリモート・メソッドを呼び出す際の手順は、次のとおりです。
- ブラウザーは、RSExecuteに対するJavaScript呼び出しを実行します。RSExecuteは、Microsoft Visual InterDevが提供している組み込みJavaScriptフレームワークに含まれています。
- Remote ScriptingアプレットはHTTPのGETを使用して、サーバー上の特別なサーブレットURLにアクセスします。その際に、メソッド名とパラメーターを指定します。
- サーブレットはXMLライクな応答を返し、アプレットはそれを受け取ります。
- 応答はRemote Scripting JavaScriptによって解釈され、呼び出し側のコードに返されます。返されるのは呼び出しオブジェクトで、メソッドが正常に実行された場合、実際の戻り値は
.return_valueプロパティーです。
図1. Microsoft Remote Scriptingのアーキテクチャー
Brent Ashley氏のJavaScript Remote Scriptingは、同じ目標を達成するために、非表示の<IFRAME> または<LAYER> (ブラウザーのタイプに応じて使い分ける) を各Remote Scripting呼び出し毎に挿入するというDHTMLの巧みなテクニックを利用しています。非表示の部分には、HTTPのGETを使用してRemote ScriptingのURLにナビゲートされます。サーバーから返される結果はHTMLで、その中にメイン・ウィンドウのコールバック関数にへのonLoad JavaScript呼び出しが埋め込まれています。
図2 に、サーバー側でサーブレットを使用しているJavaScript Remote Scriptingのアーキテクチャーを示します。
JavaScript Remote Scriptingを使用してリモート・メソッドを呼び出す際の手順は、次のとおりです。
- ブラウザーは、
jsrsExecuteに対するJavaScript呼び出しを実行します。jsrsExecuteは、外部のjsrsClient.jsファイルに含まれています (JSRSを入手するには、参考文献を参照してください)。 - jsrsClient.js内のコードは、
<IFRAME>または<LAYER>を作成 (あるいは、既存のものを再利用) し、必要なパラメーターを伴うURLによってそこにナビゲートします。 - サーブレットはHTMLの応答を返し、クライアントはそれを受け取ります。
- 返されたHTMLの
<BODY>にあるonLoadによって、返された値をパラメーターとして、指定されたコールバック関数が呼び出されます。
注: この記事で紹介する例では、MicrosoftのRemote Scriptingを同期的に使用していますが、JSRSの例と同じようなコールバックによってRemote Scriptingを非同期的に使用することも可能です。しかし、JSRSでは、同期的なメソッド呼び出しを実行することができません。
図2. Brent Ashley氏のJavaScript Remote Scriptingのアーキテクチャー
ここで説明するサーブレットは、MSRSとJSRSの両方をサポートするように設計されています。その柔軟性は、クライアント側で2つの手法を切り替えることができるということによってわかります。1つのHTMLページを、MSRSの部分 (JavaScriptとアプレット) とJSRSの部分 (1つの外部JavaScript) の両方を使って作成します。カテゴリー/サブカテゴリーの考え方を使用して、カテゴリーを選択すると、利用できるサブカテゴリーの選択肢が決まる、という動作を目標にします。HTMLの
<BODY> は次のとおりです。
リスト1. カテゴリーの選択
<BODY onLoad="javascript:categoryChanged()">
<FORM name="form1">
<TABLE>
<TR>
<TH>Remote Scripting Type:</TH>
<TD>
<input type="radio" name="clientType" value="MSRS">MSRS<br />
<input type="radio" name="clientType" value="JSRS" CHECKED>JSRS
</TD>
</TR>
<TR>
<TH>Category:</TH>
<TD>
<SELECT name="category" onChange="javascript:categoryChanged()">
<OPTION value="0" SELECTED>Category 0</OPTION>
<OPTION value="1">Category 1</OPTION>
<OPTION value="2">Category 2</OPTION>
<OPTION value="3">Error Test</OPTION>
</SELECT>
</TD>
</TR>
<TR>
<TH>Subcategory:</TH>
<TD>
<SELECT name="subcategory">
<!-- Need a placeholder until it can get loaded -->
<OPTION value="-1" SELECTED>---------------------</OPTION>
</SELECT>
</TD>
</TR>
</TABLE>
</FORM>
</BODY>
|
このHTMLについては、難しいところはありません。categoryChanged が、このHTML文書がロードされた時点と、カテゴリー・フィールドがユーザーによって変更された時点で呼び出されていることに注目してください。
categoryChanged メソッドは、次のように定義します。
リスト2.
categoryChangedメソッド
function categoryChanged()
{ if (document.form1.clientType[0].checked) {
// MSRS
var co = RSExecute("/servlet/RSExample", "getSubcategories",
document.form1.category.options[document.form1.category.selectedIndex].value);
if (co.status != 0) {
return;
}
var subcatstr = co.return_value;
populateDropDown(subcatstr);
}
else {
// JSRS
jsrsExecute("/servlet/RSExample", populateDropDown, "getSubcategories",
document.form1.category.options[document.form1.category.selectedIndex].value);
}
}
|
ここで初めてRemote Scriptingが登場し、リモート・メソッドgetSubcategories を呼び出しています。RSExecuteから返されるRemote Scriptingの "呼び出しオブジェクト(call object)" の詳細については、Microsoft Remote Scriptingの資料を参照してください (参考文献を参照)。jsrsExecute の呼び出しでは、その非同期の呼び出しが完了した時点で
populateDropDown を呼び出すように指定しています。populateDropDown の詳細については、参考文献を参照してください。
サーバー側では、サーブレットがリスト3 のように定義されています。
RSExampleサーブレットには、getSubcategories という名前のstatic型のpublicメソッドがあります (これは偶然の一致ではありません)。もちろん、この実装は単に考え方を実証するためのものですが、カテゴリーに関連するサブカテゴリーをデータベースから見つけ出すようなメソッドに容易に拡張できます。この
getSubcategories 関数で処理できるのはcatidが0、1、2の場合だけであることに注目してください。サンプル・コードには、このメソッドを無効な3という値を指定して呼び出す例が含まれています (カテゴリーのドロップダウンの "Error Test")。カテゴリー/サブカテゴリーの組み合わせが非常に多くて、JavaScriptオブジェクトとしてブラウザーに送信するのが難しい場合などに、Remote Scriptingを使用すると便利です。このサーブレットに必要な数のメソッドをいくつでも定義して、クライアントから同じように呼び出せばよいわけです。
クライアントに返されるサブカテゴリーは、"インデックス,値,インデックス,値, ..." という形式になっています。理想を言うなら、この種の情報はXMLを使用して引き渡すべきです。しかし、可能な限りどのブラウザーでも利用できる方式にするためには、XMLは選択肢として不適切です。もしアプリケーション環境がInternet Explorer 5ブラウザーに限定されているのであれば、XMLは、Remote Scriptingで情報を引き渡すための非常にスマートな方法です。
サーブレットのアーキテクチャーをできるだけ拡張可能にするために、HttpServlet を拡張する抽象クラスを作成しました。このクラスは、メソッド呼び出しを包括的にディスパッチし、Remote Scriptingのクライアント側のコードが処理できる形式に戻り値をパッケージします。MSRSおよびJSRSから発行されるHTTP GETは、
リスト4 にあるコードのようなものになります。
URLに十分な一意性があり、サーブレットではクライアント側の2種類の手法をURLで区別できるため、その両方の手法を動的に処理する1つの抽象サーブレット・クラスを作成しました。クライアント側のRemote Scriptingの2種類の手法が内部的にどのような仕組みになっているかを少しだけ紹介するために、リスト5 に、Microsoft方式のクライアント側Remote Scriptingコードが要求への応答として期待している形式を示します。
一見すると、舞台裏でXMLが使用されているように思えるかもしれません。しかし、RETURN_VALUE エレメントの
TYPE 属性の値が二重引用符で囲まれていないことに注目してください。したがって、このデータ形式はXML仕様に従っていません。一見したときの誤解はさておき、話を先へ進めましょう。
METHOD エレメントのVERSION 属性は、クライアント側では無視されますが、MicrosoftのASPというサーバー側のインプリメンテーションからの戻り値について一貫性を保つ目的で残されています。
RETURN_VALUE エレメントの値は、URL方式でエンコードされています。この値は、クライアント側のフレームワークで自動的に復元されます スペース文字が "+" ではなく "%20" に置き換えられていることに注目してください。これは、JavaScriptの復元機能に必要な動作です)。したがって、クライアント側のJavaScriptでは、単にテキスト関数を使用するだけで、応答に含まれている必要な情報を取り出すことができ、その情報に基づいて "呼び出しオブジェクト" を構築できます。その "呼び出しオブジェクト" は、RSExecuteから、呼び出し元のコードに返されます。
サーブレットは、JSRS呼び出しに対する応答も、必要とされる形式で構築します。戻り値は、リスト6 に示されています。
メソッド呼び出しの中心的な機能は、RemoteScriptingServlet (HttpServeletのサブクラス) のdoGet メソッドで実行されます。doGet のコードのうち、この後の説明に関係のある部分は次のとおりです。
リスト7.
doGet
String method;
int pcount = 0;
callbackName = request.getParameter("C");
if (callbackName != null) {
// client is JSRS - it passes a "C" parameter
clientType = JSRS;
method = request.getParameter("F");
// JSRS doesn't tell us how many parameters, so count them
while (request.getParameter("P" + pcount) != null)
pcount++;
}
else {
clientType = MSRS;
method = request.getParameter("_method");
pcount = Integer.parseInt(request.getParameter("pcount"));
}
// ... some code omitted, refer to the full code included
// find and invoke the appropriate static method in the concrete class
Class c = this.getClass();
Method m = c.getMethod(method, paramSpec);
returnValue = (String) m.invoke(null, params);
|
RemoteScriptingServletクラスに含まれるコード全体については、リスト8 を参照してください。最初に、クライアントのタイプを判別します。それには、JSRSの場合は "C"パラメーターが存在し、MSRSの場合はそれが存在しないことを手がかりにします。次の要点は、呼び出されているサーブレットのクラス (この例ではRSExample) への参照を取得し、いくつかのパラメーターに基づいて呼び出されるメソッド (getSubcategories
) への参照を取得した後、該当するパラメーターを指定してそのメソッドを呼び出すことです。ディスパッチされるメソッドへのパラメーターは、クライアントからの呼び出しのときと数が一致していなければならず、すべてString型でなければなりません (そのメソッドの内部では、必要ならString型から他の型に変換してもかまいません)。ここでは、意図的に静的メソッドへのディスパッチを選びましたが、クラス・メソッドではなくインスタンス・メソッドへのディスパッチも簡単に実行できます。invokeへの最初のパラメーターとして "this" を指定すればよいだけです。上記のコード全体をtry/catchブロックに入れてあるため、何らかの例外が発生しても、それは整然とクライアントに送り返されます。MSRSのエラーは、RETURN_VALUE エレメントのTYPE 属性にERROR を設定し、RETURN_VALUE エレメントの値としてエンコードされたエラー・テキストを指定するという形で返されます。JSRSのエラーは、onLoad でjsrsError を実行するHTMLとして返されます。
Remote Scriptingを使用して実現できる興味深い事柄はたくさんあります。Remote Scriptingの非同期の機能とJavaScriptウィンドウのタイマーを利用すれば、ブラウザーからサーバーにメッセージ、コンテンツ、またはその他の種類の更新情報をポーリングできるメッセージング・システムを構築できます。
Remote Scriptingで使用されているテクノロジーのために、それが動作するブラウザーに制限があります。MSRSを使用するには、JavaScriptから非表示のアプレットに通信してリモート呼び出しを実行しなければなりません。Java仮想マシン (JVM) が "スクリプト可能" (JavaScriptとJavaアプレットが通信できるということ) なのは、限られた数のブラウザーだけです。したがって、Win32上の (そしておそらく他のプラットフォーム上の)NetscapeおよびInternet ExplorerだけがMSRSをサポートできます。また、すべてのパラメーターがHTTPのGET要求に合わせてURL形式にエンコードされるため、パラメーターのサイズに制限があります。この制限を解消するために、HTTPのGETではなくPOSTを実行する修正版のアプレットも存在します。Brent Ashley氏のサイトから、POST版のアプレットをダウンロードできます (参考文献を参照)。
Remote Scriptingを継続的なポーリングに利用する場合には、スケーラビリティーの問題が持ち上がります。そこで、アプリケーションの必要に応じて要求の頻度を減らすか、頻度を動的に変化させることさえ考えられます。受け取るメッセージが少ないときにはポーリングの頻度を減らし、大量のメッセージを受け取るときにはポーリングの頻度を増やすという具合です。メソッド呼び出しにHTTPのGETが使用されているため、メソッドに送るデータのサイズには制限があります。リモート・メソッドに対する応答については、明示的なサイズの制限はありません。とはいえ、要求ごとにサーバーとの間でやり取りされるデータの量が多い場合は、このメッセージング・アーキテクチャーは不適切です。
Remote Scriptingメソッドのセキュリティーについて考慮する必要があります。Webブラウザーで、該当するURLを必要なパラメーターを指定して開くだけで、リモート・メソッドが呼び出されて、結果が送り返されてきます。そこで、結果を返す前に、ユーザーがアプリケーションにログインしていることを確認する必要があるかもしれません。また、異なるホスト間のスクリプトをブラウザーは利用できないというセキュリティー制限があるため、メインHTMLページ用のホストは、Remote Scriptingサーブレット用のホストと同じでなければなりません。
HTTPが使用されているため、ファイヤーウォールは通信の妨げになりません。ただし、環境によってはJavaアプレットがファイヤーウォールによってブロックされるため、MicrosoftのRemote Scriptingの動作が妨げられます。HTTPSは、クライアント側の2つの手法のどちらの場合も、問題なく利用できます。
Remote Scriptingは、Webブラウザー・ベースのアプリケーションに、デスクトップ・アプリケーションに近い感触を与えるための優れた技法です。この記事で紹介した
RemoteScriptingServlet 基底クラスは、以前にはActive Server Pageアプリケーションでしか利用できなかったテクニックを、サーバー側のJavaアプリケーションで利用するための扉を開きました。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Source code for this article | wa-resc-rsexample.zip | 5 KB | HTTP |
-
Brent Ashley氏のJSRS をダウンロードできます。
-
populateDropDownDownloadの詳細を入手できます。それには、この記事のサンプル・コード(RemoteScriptingServletクラスを含む) も含まれています。 -
JavaのリフレクションAPI の利点を調べてください。
- このサーブレットをテストするために、ApacheのTomcat を利用しました。
Erik Hatcher氏は、eHatcher Solutions, Inc. の社長です。また、最近、"ハイオクタンサイト"の設計者としてPromoFuel社にも加わりました。さらに、アリゾナ州トゥーソンのソフトウェア・デベロッパー団体で積極的に活動しており、Back Office管理者コンファレンスにおけるWindows NTセキュリティー分析に参加したり、トゥーソン開発者シリーズのXML部門に参加したりしました。彼の連絡先は、erik@hatcher.net です。