サーブレットを使用するRemote Scripting

Webアプリケーションにデスクトップ・アプリケーションに匹敵する対話性とダイナミズムを与える方法

Webアプリケーションのユーザーは、デスクトップ・アプリケーションの世界で体験している事との大きな違いに戸惑ってきました。多くのWebアプリケーションは、HTMLとHTTPの課す制約のために、スタンドアロンまたはクライアント/サーバーの標準的なデスクトップ・アプリケーションが提供している使いやすさ、対話性、そして動的な応答などをまったく取り入れていません。この記事で、Erik Hatcher氏は、Webアプリケーションの対話性と動的な応答を、Remote Scriptingを利用して向上させる方法を説明します。

Erik Hatcher (erik@hatcher.net), President, eHatcher Solutions, Inc.

Erik Hatcher氏は、eHatcher Solutions, Inc. の社長です。また、最近、"ハイオクタンサイト"の設計者としてPromoFuel社にも加わりました。さらに、アリゾナ州トゥーソンのソフトウェア・デベロッパー団体で積極的に活動しており、Back Office管理者コンファレンスにおけるWindows NTセキュリティー分析に参加したり、トゥーソン開発者シリーズのXML部門に参加したりしました。彼の連絡先は、erik@hatcher.net です。



2001年 2月 01日

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つの技法のアーキテクチャーについて詳しくみてみましょう。


Redmondスクリプト

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を使用してリモート・メソッドを呼び出す際の手順は、次のとおりです。

  1. ブラウザーは、RSExecuteに対するJavaScript呼び出しを実行します。RSExecuteは、Microsoft Visual InterDevが提供している組み込みJavaScriptフレームワークに含まれています。
  2. Remote ScriptingアプレットはHTTPのGETを使用して、サーバー上の特別なサーブレットURLにアクセスします。その際に、メソッド名とパラメーターを指定します。
  3. サーブレットはXMLライクな応答を返し、アプレットはそれを受け取ります。
  4. 応答はRemote Scripting JavaScriptによって解釈され、呼び出し側のコードに返されます。返されるのは呼び出しオブジェクトで、メソッドが正常に実行された場合、実際の戻り値は .return_value プロパティーです。
図1. Microsoft Remote Scriptingのアーキテクチャー
図1. Microsoft Remote Scriptingのアーキテクチャー

JSRSのアーキテクチャー

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を使用してリモート・メソッドを呼び出す際の手順は、次のとおりです。

  1. ブラウザーは、jsrsExecute に対するJavaScript呼び出しを実行します。jsrsExecuteは、外部のjsrsClient.jsファイルに含まれています (JSRSを入手するには、参考文献を参照してください)。
  2. jsrsClient.js内のコードは、<IFRAME> または<LAYER> を作成 (あるいは、既存のものを再利用) し、必要なパラメーターを伴うURLによってそこにナビゲートします。
  3. サーブレットはHTMLの応答を返し、クライアントはそれを受け取ります。
  4. 返された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 のように定義されています。

リスト3

リスティングを見るにはここをクリック

リスト3

public class RSExample extends RemoteScriptingServlet {
    public static String getSubcategories (String catstr) throws Exception
    {
      // sure, there is a possibility of an exception happening here, but
      // RemoteScriptingServlet will catch it and deal with it appropriately

      int catid = Integer.parseInt(catstr);

      String subcats[][] = new String[][] {
         new String[] {"Category 0 - Subcategory 0", "Category 0 - Subcategory 1", "Category 0 - Subcategory 2"},
         new String[] {"Category 1 - Subcategory 0", "Category 1 - Subcategory 1", "Category 1 - Subcategory 2"},
         new String[] {"Category 2 - Subcategory 0", "Category 2 - Subcategory 1", "Category 2 - Subcategory 2"},
      };

      String retval = "";
      for (int i = 0; i < subcats[catid].length; i++) {
        // build a string with "index,value" pairs separated by semicolons
        // for demo brevity, we'll assume that no commas or semicolons are present
        // in the values of subcats[catid][i]
        retval += i + "," + subcats[catid][i] + ";";
      }

      return retval;
    }
}

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 にあるコードのようなものになります。

リスト4

リスティングを見るにはここをクリック

リスト4

 Method
                                                       URL
 MSRS
         /servlet/RSExample?_method=getSubcategories=execute=1=1 

              where _method is the method the client is attempting to invoke, _mtype is always "execute" (and ignored by
              RemoteScriptingServlet), pcount is the number of parameters, and p0...pN are the parameters passed to the method.
 JSRS
         /servlet/RSExample?C=jsrs1=getSubcategories=[1] 

              where C is the callback context identifier used to identify which callback method gets invoked on the client side, F is
              the method to invoke, and P0...PN are the parameters passed to the method.

URLに十分な一意性があり、サーブレットではクライアント側の2種類の手法をURLで区別できるため、その両方の手法を動的に処理する1つの抽象サーブレット・クラスを作成しました。クライアント側のRemote Scriptingの2種類の手法が内部的にどのような仕組みになっているかを少しだけ紹介するために、リスト5 に、Microsoft方式のクライアント側Remote Scriptingコードが要求への応答として期待している形式を示します。

リスト5

リスティングを見るにはここをクリック

リスト5

 <METHOD VERSION="1.0.8044"><RETURN_VALUE
 TYPE=SIMPLE>0%2CCategory%201%20-%20Subcategory%200%3B1%2CCategory%201%20-%20Subcategory%201%3B2%2CCategory%201%20-%20Subcategory%202%3B</RETURN_VALUE></METHOD>

一見すると、舞台裏でXMLが使用されているように思えるかもしれません。しかし、RETURN_VALUE エレメントの TYPE 属性の値が二重引用符で囲まれていないことに注目してください。したがって、このデータ形式はXML仕様に従っていません。一見したときの誤解はさておき、話を先へ進めましょう。METHOD エレメントのVERSION 属性は、クライアント側では無視されますが、MicrosoftのASPというサーバー側のインプリメンテーションからの戻り値について一貫性を保つ目的で残されています。RETURN_VALUE エレメントの値は、URL方式でエンコードされています。この値は、クライアント側のフレームワークで自動的に復元されます スペース文字が "+" ではなく "%20" に置き換えられていることに注目してください。これは、JavaScriptの復元機能に必要な動作です)。したがって、クライアント側のJavaScriptでは、単にテキスト関数を使用するだけで、応答に含まれている必要な情報を取り出すことができ、その情報に基づいて "呼び出しオブジェクト" を構築できます。その "呼び出しオブジェクト" は、RSExecuteから、呼び出し元のコードに返されます。

サーブレットは、JSRS呼び出しに対する応答も、必要とされる形式で構築します。戻り値は、リスト6 に示されています。

リスト6

リスティングを見るにはここをクリック

リスト6

 <html>
 <head> </head>
 <body onload="p=document.layers?parentLayer:window.parent;p.jsrsLoaded('jsrs1');">jsrsPayload:
 <br />
 <form name="jsrs_Form">
 <textarea name="jsrs_Payload">0,Category 1 - Subcategory 0;1,Category 1 - Subcategory 1;2,Category 1 - Subcategory 2; 
 </textarea>
 </form>
 </body>
 </html>

メソッド呼び出しの中心的な機能は、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のエラーは、onLoadjsrsError を実行するHTMLとして返されます。

リスト8

リスティングを見るにはここをクリック

リスト8

// eHatcher Solutions, Inc

import java.lang.reflect.*;
import java.io.*;
import java.net.URLEncoder;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Base class for remote scripting servlets that use Microsoft's remote scripting (MSRS) or JavaScript remote scripting (JSRS).
 *
 * Usage:
 * <pre>
 * public class RSExample extends RemoteScriptingServlet {
 *    public static String getSubcategories (String catstr) throws Exception
 *    {
 *      String retval = "";
 *        // ... implementation details
 *      return retval;
 *    }
 * }
 * </pre>
 *
 * @version1.0  February 10, 2001
 * @authorErik Hatcher
 */

abstract public class RemoteScriptingServlet extends HttpServlet {

    boolean debugOn = false;

    private final static int MSRS = 0;
    private final static int JSRS = 1;

    private int clientType;

    /**
     * Generates the appropriate response to either an MSRS or JSRS method invocation.
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException
    {
        // Requests look like this:
        // MSRS: <servletname>?_method=method=execute=1=1
        // JSRS: <servletname>?C=callback=method=[1]

        debugOn = request.getParameter("debug") != null;
        debug("-------");

        boolean error = false;
        String returnValue;
        String callbackName = "";

        // Everything is wrapped in a try/catch block, any exception will cause the ERROR return value
        // so that the client side can deal with it gracefully
        try {
          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"));
          }

          debug("clientType = " + clientType);
          debug("Method = " + method);
          debug("pcount = " + pcount);
          // build paramSpec array with all String class items
          // build params array with the p0, p1, ...., pN request values
          Class paramSpec[] = new Class[pcount];
          Class stringClass = Class.forName("java.lang.String");
          String params[] = new String[pcount];
          if (pcount > 0) {
            for (int i=0; i < pcount; i++) {
              paramSpec[i] = stringClass;
              params[i] = request.getParameter(( (clientType == MSRS) ? "p" : "P") + i);

              if (clientType == JSRS) {
                // JSRS sends parameters wrapped with brackets, strip them off
                params[i] = params[i].substring(1, params[i].length() - 1);
              }

              debug("p" + i + " = " + params[i]);
            }
          }

          // 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);
        } catch (Exception e) {
          // if the invoked method threw an exception, pull it out of the wrapper exception that "invoke" throws
          // so that the client gets the real error message
          if (e instanceof InvocationTargetException) {
            e = (Exception) ((InvocationTargetException)e).getTargetException();
          }
          debug("Oops, exception: " + e);
          error = true;
          returnValue = e.toString();
          e.printStackTrace();
        }

        String outputString = "";
        if (clientType == MSRS) {
          // Build the appropriate MSRS response
          // Microsoft's Remote Scripting has three types: SIMPLE, EVAL_OBJECT, and ERROR.
          // Currently only SIMPLE and ERROR are supported.
          outputString = "<METHOD VERSION=\"1.0.8044\"><RETURN_VALUE TYPE=" + (error ? "ERROR" : "SIMPLE") + ">" + encode(returnValue) + "</RETURN_VALUE></METHOD>";
        }
        else {
          // Build the appropriate JSRS response
          outputString = "<html><head></head><body onload=\"p=document.layers?parentLayer:window.parent;";
          if (error) {
            outputString += "p.jsrsError('" + callbackName + "','jsrsError: " + encode(returnValue) + "');\">jsrsError: " + jsrsErrorEscape(returnValue);
          }
          else {
            outputString += "p.jsrsLoaded('" + callbackName + "');\">jsrsPayload:<br><form name=\"jsrs_Form\"><textarea name=\"jsrs_Payload\">" + jsrsEscape(returnValue) + "</textarea></form>";
          }
          outputString += "</body></html>";
        }

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println(outputString);
    }

    private void debug (String str)
    {
      if (debugOn) System.out.println("[RemoteScriptingServlet] " + str);
    }

    public static String jsrsEscape (String str)
    {
      StringBuffer sb = new StringBuffer(str);

      // probably an easier way to do this, but this will do for now
      for (int i = 0; i < sb.length(); i++) {
        if (sb.charAt(i) == '/') {
          sb.replace(i,i+1,"\\/");
          i += 2;
        }
      }

      return new String(sb);
    }

    public static String jsrsErrorEscape (String str)
    {
      StringBuffer sb = new StringBuffer(str);

      // probably an easier way to do this, but this will do for now
      for (int i = 0; i < sb.length(); i++) {
        if (sb.charAt(i) == '\'') {
          sb.replace(i,i+1,"\\'");
          i += 2;
        }
        if (sb.charAt(i) == '\"') {
          sb.replace(i,i+1,"\\\"");
          i += 2;
        }
      }

      return new String(sb);
    }
    public static String encode (String str)
    {
      // but URLEncoder.encode isn't enough... '+' should really be "%20"
      StringBuffer sb = new StringBuffer(URLEncoder.encode(str));

      for (int i = 0; i < sb.length(); i++) {
        if (sb.charAt(i) == '+') {
          sb.replace(i,i+1,"%20");
          i += 2;
        }
      }

      return new String(sb);
    }
/*
MSRS return details:

String return:
<METHOD VERSION="1.0.8044"><RETURN_VALUE TYPE=SIMPLE>%3CMessages%3E%3CMsg7%20ID%3D%221%22%3EMsg7%20data%3C/Msg7%3E%3C/Messages%3E</RETURN_VALUE></METHOD>

Error return:
<METHOD VERSION="1.0.8044"><RETURN_VALUE TYPE=ERROR>xyz%20%3A%20not%20a%20public%20function</RETURN_VALUE></METHOD>
*/

/*
JSRS return details:

Successful JSRS return:
<html><head></head><body onload="p=document.layers?parentLayer:window.parent;p.jsrsLoaded('jsrs1');">jsrsPayload:<br><form name="jsrs_Form"><textarea name="jsrs_Payload">string~TEST</textarea></form></body></html>

Error JSRS return:
<html><head></head><body onload="p=document.layers?parentLayer:window.parent;p.jsrsError('jsrs1','error');">error</body></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 articlewa-resc-rsexample.zip5 KB

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=293198
ArticleTitle=サーブレットを使用するRemote Scripting
publish-date=02012001