Ext.Direct Remotingの仕様

Ext Directはクライアント側においてサーバー側の言語に依存せずにサーバー側のメソッドを遠隔操作するためのプラットフォームであり、技術仕様でもあります。またそれだけではなく、Ext Directを利用したクライアント側のExtアプリケーションであれば、任意のサーバープログラムとの間で通信を行うことが可能です。すでに多くのサーバー側環境とのインテグレーションのためのインターフェースが実装されています:

  • PHP
  • Java
  • .NET
  • ColdFusion
  • Ruby
  • Perl

付随する機能

Ext Directの技術仕様には様々な機能が含まれています。それらを実際に実装する、あるいはしないというのはサーバー側のインターフェースを実装する開発者が判断できるようになっています。いくつかの機能についてはある言語/フレームワークでは実装する必要がないかもしれませんし、逆に言語的に実装ができない機能もあるかもしれません。

  • メタデータによりメソッドの遠隔操作 — カスタムアノテーションと属性
  • JSONあるいはXMLによるプログラム的な情報設定
  • 型変換
  • メソッドの非同期呼び出し
  • メソッドのバッチ化、実行
  • ファイルアップロード
  • 特定のイベントの前後にアクションを指定することによりアスペクト指向プログラミングを可能とする。この考え方はセキュリティ関連を含む様々な場面に応用可能なコンセプトです。

サーバー側スタック

サーバ側スタックではメソッドの遠隔操作を可能とするための以下の3つのキーコンポーネントの実装が必須となります。

  • コンフィグ – どのコンポーネントをクライアント側に対して公開するのかを設定
  • API – コンフィグをもとにクライアント側に解釈可能な形式に変換
  • ルーター – 受け取ったリクエストを適切なクラス、コンポーネント、関数に受け渡し

コンフィグ

クライアント側に対してどのクラスをを公開するのか、ということをサーバー側が通知する手段としては通常次の4通りが考えられます:プログラム的に、JSONまたXMLによるコンフィグ、あるいはメタデータ。

プログラム実行時に動的なイントロスペクションが可能な言語であれば公開するメソッドについてより少ないコンフィグ情報ですむことになります。

言語依存の検討事項:

  • 引数をオプションとして扱えるか
  • メソッドに対して実行時に動的にイントロスペクションが可能か
  • メタデータをクラスやメソッドに関連づけることが可能か
  • メソッドが受け取る引数の数の特定が必要(現行のExt.Directにおいて必要)
  • 引数のコレクションを受け渡す為に名前付き引数を指定することが可能か。コレクションを引数として利用する場合には、渡される順番は関係ないこと(現行のExt.Direct未サポート)

サーバー側のメソッドをコンフィグに記述するための方法は言語によって異なってきます。ある言語で機能したコンフィグが、別の言語では情報不足となってしまう可能性も当然考えられます。コンフィグでは必ず、遠隔操作可能なメソッドがどれなのか、それらの引数は何か、そしてどうやって実行するか、ということを出力する必要があります

プログラム的なコンフィグ

プログラム的なコンフィグとは、その言語における「キー/バリュー」のペアによるコンフィグを作るだけのことです。「キー/バリュー」のデータ構造については色々な呼び方があります(ハッシュマップ、辞書、連想配列、構造体、オブジェクトなど)ので、利用されるサーバー側言語で一番利用されている呼び方を使ってください。

  1. $API = array(
  2.   'AlbumList'=>array(
  3.       'methods'=>array(
  4.           'getAll'=>array(
  5.               'len'=>0
  6.           ),
  7.           'add'=>array(
  8.               'len'=>1
  9.           )
  10.       )
  11.   )
  12. );

上記例では、サーバー側に対して、AlbumListクラスのgetAllとaddの2つのメソッドを公開するようにコンフィグを行っています。getAllメソッドは引数を取らず、addメソッドは引数を1つだけ取ります。上記のようにPHPで実装した場合、引数の名前や型、あるいは引数の順番のような追加の情報について記述する必要はありませんが、他の言語では、こういった追加情報も必要となるかもしれません。その場合、そのような情報もコンフィグの中に記述する必要があります。

JSONとXMLによるコンフィグ

上記のコンフィグ情報についてはJSONあるいはXML形式でコンフィグファイルに記述しておき、実行時に読み出させることも可能です。

JSONコンフィグ:
  1. {
  2.     AlbumList: {
  3.         methods: {
  4.             getAll: {
  5.                 len: 0
  6.             },
  7.             add: {
  8.                 len: 1
  9.             }
  10.         }
  11.     }
  12. }


XMLコンフィグ:
  1. <AlbumList>
  2.     <methods>
  3.         <method name="getAll" len="0" />
  4.         <method name="add" len="1" />
  5.     </methods>
  6. </AlbumList>
メタデータによるコンフィグ

実行時にメソッドを動的にイントロスペクションできるようなサーバー側の言語を利用する場合はもっと少ない情報でも十分かもしれません。下記のColdFusionのコンポーネントをご覧ください。遠隔操作を可能にするためのExtDirect属性が付与されています。

  1. <cfcomponent name="AlbumList" ExtDirect="true">
  2.     <cffunction name="getAll" ExtDirect="true">
  3.         <cfreturn true />
  4.     </cffunction>
  5.     <cffunction name="add" ExtDirect="true">
  6.         <cfargument name="album" />
  7.         <cfreturn true />
  8.     </cffunction>
  9. </cffunction>

ColdFusionのExt.Directスタックはこのコンポーネントに対してイントロスペクションを行い、ExtDirect属性を介してこのコンポーネントを遠隔操作可能にするために必要な全ての情報決定することができます。

API

Ext.DirectスタックにおけるAPIコンポーネントの役割は、Ext.Directがクライアント側においてスタブを生成するために必要なメソッドのコンフィグを出力することです。このような代理のメソッドを生成することにより、クライアントとサーバー間のやり取りを気にせずに、サーバー側のメソッドをあたかもクライアント側のメソッドであるかのように呼び出すことが可能になります。

APIコンポーネントはアプリケーションのヘッド部分においてスクリプトタグ経由で読み込まれます。サーバー側は、クライアント側で実行可能なJavaScriptコードを動的に出力する必要があります。

スクリプタグ経由でのAPI読み込み
  1. <script src="Api.php"></script>

APIの出力はコンフィグで公開されたメソッドをJavaScriptコードで記述したものになります。下記の例ではExt.app.REMOTING_APIという変数名を使っています。出力内容には、メソッドにアクセスするためのURL、タイプ、そして利用可能なアクション(クラスとメソッド)が含まれます。

Api.php(あるいは他のサーバー側スタック)が出力するJavaScriptコード
  1. Ext.app.REMOTING_API = {
  2.     "url":"remote\/router.php",
  3.     "type":"remoting",
  4.     "actions":{
  5.         "AlbumList":[{
  6.             "name":"getAll",
  7.             "len":0
  8.         },{
  9.             "name":"add",
  10.             "len":1
  11.         }]
  12.     }
  13. };
ルーター

ルーターはクライアント側からのリクエストを受け付け、それを適切なクラス、メソッドに引数とともに渡します。

リクエストの送信方法は2つあります:JSONでエンコードされた生のHTTP POSTかformによるPOSTです。ファイルをアップロードする際にはformによるPOSTが必要です。

JSONでエンコードされた生のHTTP POSTの例
  1. {"action":"AlbumList","method":"getAll","data":[],"type":"rpc","tid":2}

ルーターは生のHTTP POSTをデコードする必要があります。HTTP POSTが配列で渡ってきた場合、ルーターは複数のリクエストをそれぞれのリクエスト先に配信することになります。

1つのトランザクションに含まれるもの
  • action – 利用するクラス
  • method – 実行するメソッド
  • data – メソッドへの引数 – 配列 (Ext.Directは将来的には名前付き引数、つまりオブジェクトリテラル、をサポート予定)
  • type – 全ての遠隔操作のリクエストには“rpc”を指定
  • tid – このリクエストに紐づけられたトランザクションID。1回のPOST に複数のトランザクションが含まれる場合は、それぞれ異なるtidを持つことになります。
Form POSTに含まれるもの
  • extAction – 利用するクラス
  • extMethod – 実行するメソッド
  • extTID – このリクエストに紐づけられたトランザクションID。Form POSTでは1度に1つのトランザクションのみ可能です
  • extUpload – (オプション) POSTがファイルアップロードの場合はフィールドが送られます
  • その他のフォームフィールドは全てメソッドへの引数として扱われることになります

トランザクションが無事受け付けられたら、ルータによってディスパッチされることになります。ルーターは関連するクラスを構築し、受信したデータから読み込んだ引数をもとにメソッドを呼び出します。現時点のExt.Directでは、この引数は配列であることを想定した仕様となっていますが、将来的にはオブジェクトリテラル的なキー/バリュー型の引数をサポートする予定です。

注意 現時点においては、引数をもとにクラスを構築する仕組みは用意されていません。デフォルトのコンストラクターが利用されます。

各トランザクションはその結果を下記の情報を含むキー/バリュー型のデータ構造で返す必要があります。

  • type – 'rpc'
  • tid – 実行されたトランザクションのID
  • action – 実行されたクラス/アクション
  • method – 実行されたメソッド
  • result – メソッドを呼び出した結果

レスポンスはJSONでエンコードされます。ルーターは複数のトランザクションを1つのレスポンス(配列)にまとめて返すことができます。

リクエストがForm POSTで、そしてアップロードであった場合、レスポンスは下記のようなHTMLとして返すことになります:

<html><body><textarea>{JSONでのレスポンスをここに}</textarea></body></html>

&quot; はバックスラッシュでエスケープしておく必要があります。エスケープしておかないとtextareaの中において実際のクオーテーションマークに変換されてしまうことになります。つまり、次の正規表現をつかうことになります (/"/, "\"")

レスポンスはJSONでエンコードされ、textareaの中に内包されることになります

Form POSTにおいては1回のリクエストでよびだせるメソッドは1つだけです。複数のリクエストをバッチ処理することはできません。

サーバー側において例外が発生した場合、ルーターはそのデバッグモードにおいて以下の情報を返す必要があります。

  • type – 'exception'
  • message – サーバー側で起きたエラーを特定するためのメッセージ
  • where – サーバー側のどの部分でエラーが起きたかを伝えるメッセージ

このルーターにおける例外処理機能については、機能そのものをオン・オフできるようにしておく必要があります。セキュリティ的な観点から、本番環境においてはこれらの例外情報はクライアント側に渡らないようにしておく必要があるからです。

ここでの「例外」はあくまでもサーバー側での例外であり、アプリケーションレベルでのエラーのことではありません。

クライアント側のExt.Direct

クライアント側においてExt.Directを利用するためには、APIにおいて使った変数名をプロバイダーとして登録する必要があります

  1. Ext.Direct.addProvider(Ext.app.REMOTING_API);

プロバイダーの登録後全てのアクションはグローバルスコープで利用可能となります(APIに名前空間の設定を含めることによりアクションを特定の名前空間に閉じ込めることも可能です)

この時点でクライアント側では、公開されているメソッドをあたかもクライアント側のメソッドであるかのように呼び出せるようになります。

  • AlbumList.getAll
  • AlbumList.add

メソッドを呼び出す際にはメソッドが想定する引数に加えて、もう1つコールバック処理用の引数が最後に追加されることになります。これらのサーバー側のメソッドはすべて非同期で処理されることになるため、メソッドのレスポンスを処理する為にはコールバック処理が必要となってくるわけです。メソッド呼び出しのレスポンスはすぐには帰ってきません。

つまり、下記のような書き方はできません:

  1. var albums = AlbumList.getAll();

上記のコードは下記のように書きなおす必要があります:

  1. AlbumList.getAll(function(provider, response) {
  2.    // process response
  3. });
© 2006-2010 Ext Japan, LLC