Curl プロセス間のコミュニケーション

概要

CURL.REMOTE パッケージは、異なる Curl プロセス間のコミュニケーションに使われる API を含んでいます。コミュニケーションは、メッセージのパスまたは、リモート プロシージャ コール(RPC)で行われます。サブアプレットは、このために使うことが出来る設定済みの接続を持っています。アプレットは、SocketRemoteConnectionSocketRemoteAcceptor を使用して、独自のプロセス間通信を作ることも出来ます。

リモート接続の確立

RemoteConnection は、Curl プロセス間の RemoteEvent の伝達の為の双方向通信の一つの終端です。RemoteConnection は、転送の終端を表す Curl オブジェクトのクラスですが、この用語は、しばしば転送そのものを参照する為に使われます。"RemoteConnection の一つの終端" という句は、RemoteConnection オブジェクトそのものを参照します。RemoteConnection には、方向性がありません。ですから、RemoteEvent は、RemoteConnection のいずれかの終端から他方に流れることが出来ます。いずれかの終端は、"query" または "reply" を送る(または、処理する)のに使われます。
一般的には、RemoteConnection を使用して、リモート オブジェクトにアクセスし、RemoteProxy を介して、リモート呼び出しを行い、RemoteYxorp を介して、リモート使用の為にオブジェクトを開示します。
最初に、define-remote-class マクロを使用して、リモート インターフェースを定義し、インターフェースをカスタマイズする RemoteProxyRemoteYxorp のサブクラスを作成します。
次に、RemoteConnection のペア、サブアプレット内のApplet.inline-remote-connection と親アプレット内の AppletData.inline-remote-connection、を含む様々なメカニズムを使用して、接続を確立します。そして、接続の一端は、リモート インターフェースから継承したオブジェクトを作成し、適切な RemoteYxorp 内で、それぞれをラッピングすることにより、もう一方の終端にそれらを開示します。もう一方の終端は、適切な RemoteProxy を作成し、オブジェクト.0 上で非同期リモート プロシージャ コールを実行するための remote-invoke-async マクロを使用することで、これらのオブジェクトにアクセスすることが出来ます。
各終端は、様々なリモート インターフェースを使用して、多くのオブジェクトを開示し、他の終端は、両端が使用されるインターフェースと RemoteYxorp 及び RemoteProxyによって使用される whom に同意している限り、これらのオブジェクトにアクセスすることが出来ます。
remote-invoke-sync マクロと remote-invoke-async マクロは、コードがブロックされることなく、永久にリモート メソッドを呼び出すことを許します。remote-invoke-sync マクロは、タイムアウトする前に応答が返ってこなかった場合、タイムアウトでリモート メソッド呼び出しを行い、例外をスローします。remote-invoke-sync マクロは、応答を待っている間、呼び出し元が実行するコードを記述する using 句を取ることが出来ます。remote-invoke-async マクロは、Curl の try ブロックをモデルにしています。これは、通常の catch 句と finally 句に加えて、リモート メソッド呼び出しの戻り値を受け入れる成功継続を記述する為に、receiving 句を追加しています。remote-invoke-async 内の様々な句は、応答 RemoteEvent が到着した時に非同期で呼び出されます。
これが簡単にしたサンプルです。より複雑なサンプルは以下のファイルにあります。:
d:\automated-build-temp\build\win32-atom\docs\default\examples\dguide\remote.zip
書庫からサンプル ファイルを解凍してください。サンプル ファイルを含むディレクトリに特権を与えるか、ファイルを Web サーバーにコピーする必要があります。サンプルの使用に関する情報は、拡張したサンプル を参照してください。
共通のヘルパー ファイル、"remote.scurl" で、簡単なリモート クラス Calculator を定義しており、それは、二つの整数を加算し、複雑な計算の代わりを表す10秒間のポーズが設定されています。
{import * from CURL.REMOTE}
{define-remote-class public Calculator
    {define-remote-proxy-class public CalculatorProxy}
    {define-remote-yxorp-class public CalculatorYxorp}

    {remote-method public {add x:int, y:int}:int
        {sleep 10s}
        {return x + y}
    }
}
子アプレット、"child.curl" では、Calculator を作成し、それを登録して、適切な CalculatorYxorp を作成することにより、親アプレットからのリモート呼び出しを処理出来るようにします。
{include "remote.scurl"}

{def calculator = {Calculator}}
{def connection = {get-the-applet}.inline-remote-connection}
{def yxorp = {CalculatorYxorp connection, 0, calculator}}
親アプレット、"parent.curl" は、子アプレットと適切な CalculatorProxy を作成し、それを使って同じ呼出を非同期に実行します。
{import AppletData from CURL.ENGINE.BROWSER}
{include "remote.scurl"}

{def child = {AppletData null, {url "child.curl"}}}
{def connection = child.inline-remote-connection}
{def proxy = {CalculatorProxy connection, 0}}

{bold Initiating remote asynchronous call...}
{remote-invoke-async
    {proxy.add 1, 2}
    receiving result:int do
    {popup-message "Remote result = " & result}
    catch ex:Exception do
    {popup-message "Remote problem: " & ex.message}
}
{bold Initiating remote asynchronous call... done.}
同期呼び出しの間、親アプレットはロードを停止し、フリーズしたように見え、再描画されませんが、非同期呼び出しの間、親アプレットはロードを継続し、再描画されることに注意してください。

ソケットリモート接続

最も一般的に RemoteConnection を使用するのは、サブアプレット内の Applet.inline-remote-connection と親アプレット内の AppletData.inline-remote-connection です。パブリック API によって提供される RemoteConnection を作成する唯一他の方法は、SocketRemoteConnection を作成することです。 SocketRemoteConnection は、ホスト OS のソケットを確立する RemoteConnection で、 SocketConnection によってカプセル化されます。転送プロトコルは、現時点では TCP です。Curl リモート システムは、暗黙的に TCP の信頼性が高い仮想回路特性に依存しますが、TCP の詳細を露出しません
Curl プロセスは、SocketRemoteConnection を使用して、二つの Curl プロセス間の接続を確立することが出来ます。Curl プロセスは、最初の AcceptorTCPSocket の作成(これは、TCP ネットワーク ソケットのリスナーを表す Curl オブジェクトです。)によって SocketRemoteConnection を確立します。 SocketRemoteAcceptor は、リスナーがネットワーク ソケット接続を受け入れた時に、accept-proc の呼び出しの中で、AcceptorTCPSocket をラップします。SocketRemoteAcceptor は、下層のネットワーク リスナーが接続を受け入れた時に、結果の新しい SocketRemoteConnection を登録された accept-proc に渡します。

リモートイベント

リモート システムの基本的なメカニズムは、接続の二つの終端間で RemoteEvent を送ることです。RemoteEvent は、RemoteConnection を通じて、別の Curl プロセスに送ることが出来る Curl の Event です。RemoteEvent.kind フィールドの整数値によって識別される3種類の RemoteEvent があります。最も重要な二つの RemoteEvent は、"query" と "reply" です。もう一つは、RemoteConnection を管理する為の実装によって使用される "other" kind です。"query" は、正の kind 値です。kind N の query に対する reply は、負の kind -N を持ちます。kind 値 0 は、kind を必要としない、"blind query" を示します。小さな kind は、エラー状況のコミュニケーションの為に予約されています。
RemoteEvent はまた、query または、reply の受信者を示す整数の whom 値、RemoteEvent の目的を示す整数の code 値、バイナリ ペイロードである data を含みます。構造化されたデータは、RemoteEventdata ペイロードにシリアル化または、"マーシャル"されます。kindwhomcode は、リモート システムがパースし、解釈することが出来る RemoteEvent ヘッダーを構成します。data は、RemoteEvent ヘッダー フィールドに応じて送られた解釈されないペイロードです。ペイロード データは、リモート接続の終端で知られた形式のフォーマットでエンコードされなければなりません。
RemoteProxy は、RemoteConnection を使用して、query を送り、reply を処理するインターフェースです。RemoteYxorp は、query の処理と reply の送信の RemoteConnection に対するインターフェースです。RemoteConnection は、複数の RemoteProxyRemoteYxorp のペアに接続することが出来ます。ペアは、整数の whom で識別されます。RemoteProxy クラスと RemoteYxorp クラスは、それぞれ RemoteConnectionwhom 値、幾つかのブックキーピング メソッドをカプセル化します。ブックキーピング メソッドに加えて、RemoteProxy は、抽象 RemoteProxy.handle-reply メソッドを宣言し、RemoteYxorp は、抽象 RemoteYxorp.handle-query を宣言します。
RemoteConnection は、RemoteEvent.kindRemoteEvent.whom の値に応じて、受け取った RemoteEvent をデスパッチします。RemoteEvent.kind が query を示す時(正の値なので)、RemoteConnection は、RemoteYxorp のテーブル内で RemoteEvent.whom の値を探し、見つかった RemoteEvent を渡すオブジェクト上で RemoteYxorp.handle-query を呼び出します。RemoteEvent.kind が、reply を示す時、RemoteConnection は、RemoteProxy のテーブル内で RemoteEvent.whom 値を探し、RemoteEventRemoteProxy.handle-reply メソッドに渡します。
RemoteProxy.handle-reply メソッドと RemoteYxorp.handle-query メソッドの両方は、RemoteEvent を受け取り、これらのメソッドが RemoteEventRemoteEvent.codeRemoteEvent.data の部分を接続の両端でどのように解釈するかは、RemoteProxyRemoteYxorp のサブクラスの実装に依ります。これらのメソッドは、一般的に RemoteProxy オブジェクトと RemoteYxorp オブジェクトのオーナー間で同意されたマッピングに応じて、RemoteEvent.code 値をディスパッチします。SimpleRemoteProxy クラスと SimpleRemoteYxorp クラスは一つのアプローチを説明します。それは、それぞれの RemoteConnection に到着した RemoteEvent のハンドラの登録許可します。通常、RemoteProxy RemoteYxorp のサブクラスは、define-remote-class によって生成され、サブクラスは、これらの詳細の全てを処理します。
SimpleRemoteProxy.send-query メソッドは、RemoteEventcode 値と data 値とquery に対する reply が戻ってきた時に呼び出される reply-handler プロシージャを取ります。SimpleRemoteProxy.send-query は、RemoteEvent 内の code と data をラップし、SimpleRemoteProxywhom と生成された kind を加算し、query の reply が帰ってきた時に呼び出される reply-handler を記録します。スレッドは、受信 query の code とプロシージャのマッピングを許可する一つ以上の query-handler プロシージャを登録することが出来ます。応答 RemoteEvent が、query の発信者に届いた時、SimpleRemoteProxy は、記録した reply-handler プロシージャを見つけ、それを応答 RemoteEvent に渡します。そして、reply-handler を記録から削除します。
CURL.REMOTE パッケージは、RemoteConnectionRemoteProxyRemoteYxorp に作成されたリモート メソッド呼び出し能力をエクスポートします。define-remote-class マクロは、 RemoteConnection を通じて実装することが出来るリモート インターフェース抽象クラスを定義する方法を提供します。define-remote-class 形式内の各 remote-method は、ユニークな code 値によって識別されるインターフェース クラス内でメソッドを生成します。define-remote-class の内部で define-remote-proxy によって 生成される RemoteProxy サブクラスは、メソッド呼び出しパラメータをマーシャルし、新しい kind を生成し、それらを whom 値とメソッドを識別する code と共に query にパッケージ化し、結果の RemoteEvent を元になる RemoteConnection を通じて送り、応答を待ちます。また、対応する RemoteYxorp を生成する define-remote-class の内部で define-remote-yxorp 形式もあります。RemoteYxorpのサブクラスは、スレッドが query を処理するハンドラを登録することができ、応答 RemoteEvent を proxy に返します。
define-remote-class によって生成される RemoteProxyRemoteYxorp のサブクラスは、RemoteConnection.prepare-dataRemoteConnection.analyze-data を使用して、引数をマーシャル イン及びアウトして、値を返します。既定では、これらの呼び出しは、限られた型の選択を処理する CURL.IO.MARSHAL 内のマーシャルを使用します。しかし、コードは、接続の両端で RemoteConnection.use-simple-serialization を呼び出すことができ、それは、CURL.IO.SERIALIZE から、より広範囲なシリアル化のサポートを使用します。アプリケーションは、また RemoteConnection.prepare-data-hookRemoteConnection.analyze-data-hook にカスタム プロシージャを設定することで、独自のマーシャル スキームを使用することが出来ます。

高度な情報

より進んだユーザーは以下の情報にも興味があるかもしれません。:
RemoteConnection.send-event メソッドは、RemoteEvent を送るのに使用することが出来ます。このメソッドは、抽象であり、サブクラスで定義されなければなりません。すぐにイベントを送ることが出来ますが、特に非同期オープンが進行中の場合または、元のバッファが一時的に満杯の場合には、このメソッドは、後で送るようにイベントまたはイベントの一部をキューに入れます。例えば、
以下のメソッドを使用して受信 RemoteEvent を処理することが出来ます:
既定では、RemoteConnection.handle-query メソッドと RemoteConnection.handle-reply メソッドはRemoteConnection.handle-other メソッドが、単に RemoteConnection.destroy 呼び出している間、登録された proxy ハンドラと yxorp ハンドラに送信します。RemoteConnection.handle-event は、、他の三つのメソッドの内から、RemoteEvent.kind に基づき適切なものを呼び出します。
RemoteConnection.destroy メソッドを呼び出すことで RemoteConnection を "destroy" することが出来ます。RemoteConnection.destroy メソッドは、RemoteConnection.close メソッドを呼び出し、RemoteConnection.destroyed? フィールドを設定し、全ての proxy オブジェクトと yxorp オブジェクトを破棄し、全ての "close-procs" を呼び出します。 破棄された接続にイベントは送られません。以後全ての受信イベントは無視されます。このメソッドは、通常明示的に呼び出されません。
RemoteConnection は、RemoteConnection.close メソッドを呼び出すことで閉じることが出来ます。RemoteConnection.close メソッドは、RemoteConnection.open? フィールドが、true であると想定し、RemoteConnection.open? フィールドを false に設定し、特別なイベントを接続の他の終端に送ります。この状態では、特別な "close" イベントが受信された時、送信者が失敗したと想定するので、受信 reply と blind query は処理されますが、query は送られず、応答が必要な受信 query は無視されます。"close" イベントは、他の終端でその RemoteConnection.destroy メソッドの呼び出しを発生させます。RemoteConnection.destroy メソッドは、この接続の終端に特別なイベントを送り返し、RemoteConnection.destroy メソッドの呼び出しを発生させます。
RemoteConnection は、RemoteConnection.opening? フィールドが、接続がまだ完了していないことを示す、 true の間、”非同期”で接続されている可能性があります。接続にイベントを送ることや、接続を閉じることが出来ますが、接続が完了するまで実際には何も発生しません。接続が完了した時に、全てのペンディング中のイベントが送られます。
最後に、特別な"close"イベント(または不正なイベント)が受信された時、RemoteConnection.destroy が呼び出されます。
接続は、”非同期”モードで作成出来る場合があり、接続がまだ完了していない(またはキャンセルされた)時、RemoteConnection.opening?true になります。この状態の場合、query の送信をしようとすると、実際には、オープンが完了した時には送信する為に、また、オープンがキャンセル失敗した時には、失敗応答を作成する為に、query はキューに入れられます。この状態で、接続を閉じようとすると、単に接続は閉じているとマークされ、接続が完了し、キューに入っている query が送信され、特別な"close"イベントが送信されるようになります。この状態で接続を破棄しようとすると、接続が中断します。