specから引用するとJSONRPCは、"同一プロセス、ソケットやhttp、多くのさまざまなメッセージパッシング環境において使用可能という概念においてトランスポート非依存"です。
この非依存性をモデル化するために、jsonrpc
ライブラリーはリモートのJSONのエンドポイントへの接続の表現にjsonrpc-connection
クラスのオブジェクトを使用します(Emacsのオブジェクトシステムの詳細はEIEIO in EIEIOを参照)。これはオブジェクト指向の現代的な用語では“抽象的(abstract)”なクラス、すなわち有用な接続オブジェクトの実クラスは常にjsonrpc-connection
のサブクラスになります。それにも関わらず、jsonrpc-connection
クラスを中心に2つのAPIを個別に定義できます。
このシナリオでは野心的な新しいJSONRPCベースのアプリケーションが、エンドポイント間で取り交わされるJSONRPCメッセージのトランスポートを提供するjsonrpc-connection
の具体的なサブクラスを選択したものとする。
アプリケーションはmake-instance
を用いてそのサブクラスのオブジェクトを作成する。リモートのエンドポイントとの接続を開始するために、アプリケーションはそのオブジェクトをjsonrpc-notify
、jsonrpc-request
、jsonrpc-async-request
のような関数に渡す。
リモートで開始された接続(通常は非同期で到来)を処理するには、make-instance
によるインスタンス化においてEIEIOのキーワード引数:request-dispatcher
および:notification-dispatcher
を用いて初期化する必要がある。これらはいずれも接続オブジェクト、リモート呼び出しされるJSONRPCメッセージを命名するシンボル、JSONRPCのparams
オブジェクトという3つの引数をもつ。
:request-dispatcher
として渡される関数はリモートのエンドポイントのリクエストを処理する役目をもち、ローカルのエンドポイント(この例では構築中のアプリケーション)からのリプライを期待する。この関数の内部ではローカルにリターン(通常のリターン)、あるいは非ローカルにリターン(エラーをthrow)できる。リクエストディスパッチャーからどちらでexitしたとしても、トランスポートを通じてリモートのエンドポイントにリプライが送信される。
通常のリターンは成功レスポンスと判断される。リターン値はJSONとしてシリアライズ可能なLispオブジェクトでなければならない(JSON値の解析と生成を参照)。この結果はJSONRPCのresult
オブジェクトとしてサーバーにフォワードされる。非ローカルなリターンは関数jsonrpc-error
を呼び出すことによって行われる。これによりエラーのレスポンスがサーバーに送信される。JSONRPCのerror
に付随する詳細には、jsonrpc-error
に渡されるものすべてが含まれる。他のタイプの予期せぬエラーからトリガーされた非局所的なリターンでも、(
debug-on-error
をセットしていなければ)エラーレスポンスを送信して、この場合にはLispデバッガが呼び出される。エラーによるデバッガへのエンターを参照のこと。
jsonrpc
ライブラリーを使用して、“準JSONRPC”として記述されたトランスポートプロトコルベースのアプリケーションを構築することは可能である。これらは似てはいるが、DAP (Debug Adapter
Protocol)のようにJSONRPCと完全に同一ではない。これらのプロトコルはリクエスト、レスポンス、通知メッセージも定義しているがフォーマットはJSONRPCと完全に同一ではない。JSONRPCの内部表現とエンドポイントが受け入れる表現を変換するように、ジェネリック関数jsonrpc-convert-to-endpoint
およびjsonrpc-convert-from-endpoint
をカスタマイズできる(ジェネリック関数を参照)。
このシナリオでは基盤として異なるトランスポートストラテジーを実装するためにjsonrpc-connection
をサブクラス化する(サブクラス化する方法についての詳細はInheritanceを参照)。その後にアプリケーション構築インターフェースのユーザーは、(
make-instance
関数を使用して)その具象クラスのオブジェクトをインスタンス化して、そのストラテジーを使用してJSONRPCエンドポイントに接続できる。ビルトインのトランスポート実装については、プロセスベースのJSONRPC接続を参照のこと。
このAPIには必須部分とオプション部分がある。
JSONRPC接続(通知やリクエスト)を開始したりエンドポイントのリクエストへのリプライをユーザーに許すには、その新たなトランスポート実装がジェネリック関数jsonrpc-connection-send
を新たなサブクラス用に特化して実装しなければならない(ジェネリック関数を参照)。このジェネリック関数はjsonrpc-request
やjsonrpc-notify
のようなプリミティブから自動的に呼び出される。この特化によって引数リストに記述されたメッセージが、基礎となる通信メカニズム(“wire”とも呼ばれる)を介して送信されて、新たなトランスポートがエンドポイントとの対話に使用することを保証する必要がある。この“wire”はネットワークソケット、シリアルインターフェイス、あるいはHTTP接続などかもしれない。
同様に3種類のリモートコンタクト(リクエスト、通知、ローカルリクエストへの応答)を処理するために、トランスポート実装は“wire”上のJSONRPC(あるいは準JSONRPC)の作成に用いられるかもしれないJSONRPCメッセージに気づいたら、Elispから関数jsonrpc-connection-receive
が呼び出されるように計らわなければならない。
最後にオプションとしてjsonrpc-connection
サブクラスはジェネリック関数jsonrpc-shutdown
およびjsonrpc-running-p
にたいして、これらの概念をトランスポートに適用する場合には、これらのジェネリック関数を特化する必要がある。jsonrpc-shutdown
の特化によって、wire上でのメッセージのlistenに用いたすべてのシステムリソース(プロセス、タイマー等)を確実にリリースすること、jsonrpc-running-p
の特化によって、これらのリソースがまだアクティブなのか、(jsonrpc-shutdown
やその他を通じて)すでにリリース済みかを伝えるように実装する必要がある。