RMIプロトコルは、ワイヤー上の形式のために、他に2つのプロトコルを利用します。Javaオブジェクト直列化プロトコルとHTTPプロトコルです。 オブジェクト直列化プロトコルは、呼出しと戻りのデータを整列化するために使用します。 HTTPプロトコルは、リモート・メソッド呼出しをPOSTし、条件が許せば戻り値を取得するために使用します。 それぞれのプロトコルは、別々の文法としてドキュメント化されます。 生成規則の非ターミナル・シンボルは、別のプロトコルが制御する規則を参照します(オブジェクト直列化またはHTTP)。 プロトコルの境界を越える場合は、それ以後の生成規則は、組み込まれたプロトコルを使用します。
RMIのワイヤー形式はStreamで表現されます。 ここで採用している用語は、クライアントからみたものです。 Outは出力メッセージを表し、Inは入力メッセージを表します。 トランスポート・ヘッダーの内容は、オブジェクト直列化を使用した形式ではありません。
Stream:
Out
In
RMIの入力と出力ストリームは対になっています。 各Outストリームは、対応するInストリームを持ちます。 Outストリームは、文法的にはソケットの出力ストリームにマッピングします(クライアントから見た場合)。 Inストリームは、文法的には、対応するソケットの入力ストリームと対になります。 出力ストリームと入力ストリームは対になっているので、入力ストリームで必要になるヘッダー情報は、プロトコルを認識できたかどうかの確認のみです。それ以外のヘッダー情報(マジック番号やバージョン番号)は、ストリーム対のコンテキストに含めることが可能です。
RMIにおける出力ストリームは、トランスポートHeader情報とそれに続くMessagesの並びから構成されます。 交互に出力ストリームはHTTPプロトコルに組み込まれた呼出しを含めることができます。
Out:
Header Messages
HttpMessage
Header:
0x4a 0x52 0x4d 0x49 Version Protocol
Version:
0x00 0x01
Protocol:
StreamProtocol
SingleOpProtocol
MultiplexProtocol
StreamProtocol:
0x4b
SingleOpProtocol:
0x4c
MultiplexProtocol:
0x4d
Messages:
Message
Messages Message
Messagesは特定のプロトコル内で、Protocolが指定する方法に従ってラップされます。 SingleOpProtocolの場合は、Headerに続くMessageが1つしかない場合があり、Messageがラップされる追加データが存在しないことがあります。 SingleOpProtocolはHTTP要求に組み込まれた呼出しのために使用されます。単一の要求と応答以上の相互作用はありません。
StreamProtocolとMultiplexProtocolの場合、サーバーはプロトコルのサポートを確認するバイト0x4eで応答し、サーバーが見ることができるホスト名とポート番号を含むEndpointIdentifierはクライアントによって使用されています。 この情報を使ってクライアントは、セキュリティ上の理由でできない場合もありますが、自分のホスト名を知ることができます。 クライアントはそのあと、もう1つのEndpointIdentifierで応答する必要があり、応答の中には接続を受け付けるときのデフォルトの終点が含まれます。 MultiplexProtocolでは、サーバーはこの情報からクライアントを特定できます。
StreamProtocolの場合は、このエンド・ポイントのネゴシエーションのあとに、それ以上のデータのラッピングを行うことなしにMessagesが出力ストリームへ送られます。 MultiplexProtocolでは、ソケット接続は、多重接続の具体的な接続として使用されます(「セクション10.6、"RMI多重化プロトコル"」で説明されています)。 この多重化された接続上で開始された仮想接続は、次に説明される一連のMessagesから構成されます。
現在、入力メッセージには、Call、PingおよびDgcAckという3つのタイプがあります。 Callは、メソッド呼出しをエンコードします。 Pingは、トランスポートレベルのメッセージであり、リモート仮想マシンの状態をテストします。 DgcAckは、サーバーからの戻り値のリモート・オブジェクトがクライアントによって受信されたことを示すサーバー分散ガベージ・コレクタに送られる確認応答です。
Message:
Call
Ping
DgcAck
Call:
0x50 CallData
Ping:
0x52
DgcAck:
0x54 UniqueIdentifier
現在、入力メッセージには、ReturnData、HttpReturn、およびPingAckの3種類があります。 ReturnDataは通常のRMI呼出しの結果です。 HttpReturnはHTTPプロトコルに組み込まれた呼出しに対する戻り値です。 PingAckはPingメッセージに対する肯定応答です。
In:
ProtocolAck Returns
ProtocolNotSupported
HttpReturn
ProtocolAck:
0x4e
ProtocolNotSupported:
0x4f
Returns:
Return
Returns Return
Return:
ReturnData
PingAck
ReturnData:
0x51 ReturnValue
PingAck:
0x53
RMI呼出しにおける呼び出しと戻りのデータは、Javaオブジェクト直列化プロトコルに従って整形されます。 それぞれのメソッド呼出しのCallDataは、Javaオブジェクトの出力ストリームに書き込まれ、それにはObjectIdentifier (呼出しの対象)、Operation (呼び出されるメソッドを表す数値)、Hash (クライアント・スタブとリモート・オブジェクト・スケルトンが共通なスタブ・プロトコルを使うことを確認する数値)、およびそれに続くArguments (ない場合もある)が含まれます。
JDK 1.1のスタブ・プロトコルでは、Operationはrmicが割り当てたメソッド番号を表し、Hashはスタブのインタフェース・ハッシュであるスタブおよびスケルトンのハッシュを表しています。 Java 2のスタブ・プロトコル(Java 2のスタブはrmicで-v1.2オプションを使用して生成される)では、Operationは -1という値を持ち、Hashは呼び出されるメソッドを表すハッシュになります。 ハッシュについては、セクション"RemoteRefインタフェース"で説明しています。
CallData:
ObjectIdentifier Operation Hash Arguments[opt]
ObjectIdentifier:
ObjectNumber UniqueIdentifier
UniqueIdentifier:
Number Time Count
Arguments:
Value
Arguments Value
Value:
Object
Primitive
RMI呼出しのReturnValueは、正常か例外かを示すリターン・コード、戻り値にタグ付けするUniqueIdentifier (必要ならばDGCAckを送るために使用する)、そして返される結果である、戻り値ValueまたはスローされたExceptionが続く構成になっています。
ReturnValue:
0x01 UniqueIdentifier Value[opt]
0x02 UniqueIdentifier Exception
ノート: ObjectIdentifier、UniqueIdentifier,およびEndpointIdentifierは、デフォルトの直列化を使用して書き出されませんが、それぞれ独自の特殊なwriteメソッド(これはオブジェクトの直列化で使用されるwriteObjectメソッドではありません)を使用します。各タイプの識別子のwriteメソッドは、そのコンポーネント・データを連続して出力ストリームに追加します。
RMIは、ObjectOutputStreamのannotateClassメソッドおよびObjectInputStreamのresolveClassメソッドをそれぞれオーバーライドします。 各クラスには、コード・ベースのURL (クラスをロードする元の場所)を使用して注釈が付けられています。 annotateClassメソッドでは、クラスをロードしたクラス・ローダーに対し、そのクラス・ローダーのコード・ベースのURLを問い合わせます。 クラス・ローダーが非nullで、非nullコード・ベースを持っている場合は、そのコード・ベースは、ObjectOutputStream.writeObjectメソッドを使用してストリームに書き込まれます。それ以外の場合は、writeObjectメソッドを使用して、ストリームにnullが書き込まれます。 注意: 最適化のため、javaパッケージ内のクラスには、注釈が付けられません。これは、これらのクラスは受信側が常に利用できるからです。
クラスの注釈は、直列化復元中にObjectInputStream.resolveClassメソッドを使用して解決されます。 resolveClassメソッドは、最初にObjectInputStream.readObjectメソッドを使用して、注釈を読み取ります。 注釈(コード・ベースURL)がnullでない場合は、そのURLのクラス・ローダーを取得して、クラスをロードしようとします。 クラスは、クラス・バイトを取り出すためにjava.net.URLConnectionを使用してロードされます。これは、Webブラウザのアプレット・クラス・ローダーが使用するメカニズムと同じです。
プロキシ経由のファイアウォール経由のRMI呼び出しの実装は、JDK 9では削除されています。
この表では、RMIで使用されるアプリケーション固有の値を表現する非ターミナル・シンボルを示します。 この表では、それぞれのシンボルがそれぞれの持つ型に対応しています。 各シンボルは、それが埋め込まれるプロトコルを使用して整形されます。
| 記号 | type |
|---|---|
Count |
short |
Exception |
java.lang.Exception |
Hash |
long |
Hostname |
UTF |
Number |
int |
Object |
java.lang.Object |
ObjectNumber |
long |
Operation |
int |
PortNumber |
int |
Primitive |
byte, int, short, long ... |
Time |
long |
多重化の目的は、エンド・ポイントの一方だけがほかの機能(TCP接続など)を使って双方の接続を開くことができる環境において、2つのエンド・ポイントが相互に、他端に対して多重の全二重接続を開くことができるモデルを提供することです。 RMIにより、この単純な多重化プロトコルを使って、ほかの方法では不可能な状況においても、クライアントがRMIサーバー・オブジェクトに接続できるようになります。 たとえば、アプレット環境のセキュリティ・マネージャの中には、入ってくる接続を受信するためのサーバー・ソケットの作成を禁止して、アプレットがRMIオブジェクトをエクスポートして直接ソケット接続からのリモート呼出しをサービスできないようにするものがあります。 アプレットがcodebaseホストへの通常のソケット接続を開くことができる場合には、その接続上で多重プロトコルを使うことにより、そのアプレットがエクスポートしたRMIオブジェクトのメソッドを、codebaseホストが呼び出せるようになります。 このセクションでは多重プロトコルの形式と規則を説明します。
このセクションでは、後述のプロトコルの説明で使用される用語を定義します。
エンド・ポイントとは、多重プロトコルを使って接続されている2人のユーザーの一方を指します。
多重プロトコルは、双方向の、信頼できる既存のバイト・ストリームの上位層になります。また、このバイト・ストリームは、エンド・ポイントの一方が他方に対して開いたものです。 現在のRMIの使用法では、これは常にjava.net.Socketオブジェクトで作られたTCP接続です。 この接続のことを具象接続と呼びます。
多重プロトコルでは、仮想接続機能を使用できます。仮想接続は、双方向の信頼できるバイト・ストリームであり、2つのエンド・ポイント間の特定のセッションを表現するものです。 2つのエンド・ポイント間の1つの具象接続上の仮想接続のセットが多重接続を構成します。 多重プロトコルを使用して、どちらのエンド・ポイントからでも仮想接続のオープンとクローズが行えます。 仮想接続の、あるエンド・ポイントに関する状態は、具象接続上でやり取りされる多重プロトコルの要素により定義されます。 この状態には、接続のオープンまたはクローズ、行き来する実際のデータ、および関連するフロー制御メカニズムが含まれます。 特に他の説明がない場合、この項のこれ以降の部分では接続という用語は仮想接続の意味で使用されます。
ある多重接続の中の仮想接続は16ビット整数で特定され、これを接続識別子と呼びます。 したがって、1つの多重接続上には、最大65,536の仮想接続が存在することになります。 同時に使用できる仮想接続の数は実装によって制限されることがあります。
接続は、種々の多重プロトコルにより定義されたオペレーションにより操作されます。 プロトコルで定義されたオペレーションには、OPEN、CLOSE、CLOSEACK、REQUEST、およびTRANSMITという名前のものがあります。 すべての操作の正確な形式と規則は、「セクション10.6.3、"プロトコル・フォーマット"」で詳しく説明しています。
OPEN、CLOSE、およびCLOSEACKオペレーションは接続を開いたり閉じたりし、REQUESTとTRANSMITオペレーションは開いた接続上でフロー制御メカニズムの制限下でのデータ伝送に使われます。
仮想接続は、エンド・ポイントがOPENオペレーションをその接続に対して送り、その接続に対するOPENオペレーションを受け取ると(ただし、そのあとにクローズされていない場合)、その特定のエンド・ポイントに対してオープンした状態になります。 種々のプロトコル・オペレーションを次に説明します。
仮想接続は、エンド・ポイントが接続にCLOSEオペレーションを送ったが、まだそれに続くCLOSEまたはCLOSEACKを受け取っていないときに、そのエンド・ポイントに対してペンディング・クローズの状態にあります。
仮想接続は開かれなかったとき、あるいはCLOSEやCLOSEACKオペレーションを受け取ったときに(そしてそれ以後オープンされていないならば)、その特定のエンド・ポイントに対してクローズの状態にあります。
多重プロトコルでは、単純パケット・フロー制御メカニズムを使用することにより、同じ具象接続上に複数の仮想接続が同時に存在することが認められます。 フロー制御メカニズムにおける高度な要件として、仮想接続の状態は他から独立していなければならないという要件があります。つまり、1つの接続の状態は、他の接続に対して影響を及ぼしません。 たとえば、ある接続から入ってくるデータを扱うバッファがいっぱいになったとしても、これが他の接続のデータ伝送やデータ処理を妨げることはありません。 このことは、1つの接続の進行が、他の接続の使用の終了に依存する場合に必要です。たとえば、RMIの再帰的な呼出しなどの場合です。 したがって、実際問題として、接続の実装は具象接続上に入ってくる多重プロトコル・データ(この仕様を満たしていると仮定して)を常に消費および処理できる必要があります。
各エンド・ポイントは、それぞれの接続に関して2つの状態値を持ちます。1つ目の状態値は、エンド・ポイントが何バイトのデータを要求し、そのうちの何バイトをまだ受け取っていないか(入力要求カウント)を示し、2つ目の状態値は、エンド・ポイントが何バイトのデータを要求し、その中でこちらのエンド・ポイントが何バイトを残しているか(出力要求カウント)を示します。
1つのエンド・ポイントの出力要求カウントは、もう一方のエンド・ポイントからREQUESTオペレーションを受けるたびに増加し、TRANSMITオペレーションを送ると減少します。 1つのエンド・ポイントの入力要求カウントは、それがREQUESTオペレーションを送ると増加し、TRANSMITオペレーションを受信すると減少します。 どちらかの数値がマイナスになるとプロトコル違反です。
1つのエンド・ポイントが、入力要求カウントを、現時点でブロックされることなく処理できるバイト数以上に、増加させてしまうほど大きなREQUESTオペレーションを送ることはプロトコル違反です。 しかし、接続しているユーザーがデータを読み込むために待機している状態では、入力要求カウントは必ずゼロ以上の値でなければなりません。
1つのエンド・ポイントが、出力要求カウントを超えたTRANSMITオペレーションを送るのはプロトコル違反です。 送出されるデータが、その接続のユーザーが明示的にフラッシュを要求するまで、バッファリングされる可能性があります。 明示的なフラッシュ要求、または実装の出力バッファがいっぱいであるために、その接続を経由してデータを送られなければならない場合は、接続上のユーザーはTRANSMITオペレーションが十分に進行するまでブロックされる可能性があります。
ここで説明した規則以外でも、適切と判断される範囲内で自由にREQUESTやTRANSMITオペレーションを実行する実装が可能です。 たとえば、エンド・ポイントは自分の受信バッファが空でなかったとしても接続に対して追加データを要求できます。
多重プロトコルのバイト・ストリーム形式は、可変長レコードが隣接して繋がったものです。 レコードの最初のバイトは、オペレーション・コードであり、そのレコードに対するオペレーションを明示し、それ以後の内容の形式を決定します。 許されるオペレーション・コードは次のとおりです。
| value | name |
|---|---|
| 0xE1 | OPEN |
| 0xE2 | CLOSE |
| 0xE3 | CLOSEACK |
| 0xE4 | REQUEST |
| 0xE5 | TRANSMIT |
レコードの最初のバイトが上に定義されたオペレーション・コード以外のときは、プロトコル違反になります。 次のセクションでそれぞれのオペレーション・コードに対するレコード形式を説明します。
OPENオペレーションの形式を次に示します。
| サイズ(バイト) | name | description |
|---|---|---|
| 1 | opcode | オペレーション・コード(OPEN) |
| 2 | ID | 接続識別子 |
エンド・ポイントは、OPENオペレーションを送ることによって、指定された接続を開きます。 IDが指示する接続がすでにオープンしていたり、送り側のエンド・ポイントに対してペンディング・クローズの状態でこのコマンドを送ったりするのは、プロトコル違反です。 接続がオープンされたあとでは、その接続に対する入力および出力の要求カウントの状態は、両側のエンド・ポイントでゼロになっています。
OPENオペレーションの受信は、他端のエンド・ポイントが指定の接続を開こうとしていることを示します。 接続がオープンされたあとでは、その接続に対する入力および出力の要求カウントの状態は、両側のエンド・ポイントでゼロになっています。
両側のエンド・ポイントでの識別子の競合を防止するために、有効接続識別子のためのスペースはMost significant bitの値によって2つに分かれています。 それぞれのエンド・ポイントは、高位ビットが特定の値を持っている場合にだけ、接続を開くことが許されます。 具象接続を開始しようとするエンド・ポイントは、最高位ビットが設定された識別子でだけ接続を開くことができ、他端のエンド・ポイントは、最高位ビットがゼロに設定された識別子で接続を開かなければなりません。 たとえば、サーバー・ソケットを作成できないRMIアプレットが自分のcodebaseホストに対して多重化接続を開始しようとするときは、アプレットは仮想接続を識別子0x8000-7FFFの範囲で開くことができ、サーバーは識別子の範囲0-0x7FFFの範囲で仮想接続を開くことができます。
CLOSEオペレーションの形式を次に示します。
| サイズ(バイト) | name | description |
|---|---|---|
| 1 | opcode | オペレーション・コード(OPEN) |
| 2 | ID | 接続識別子 |
エンド・ポイントは、CLOSEオペレーションを送ることによって、指定の接続を閉じます。 IDが指示する接続が現在閉じていたり、送信側に対してペンディング・クローズになっているとプロトコル違反になります(この接続に対してCLOSEオペレーションを送っていると、受信側に対してペンディング・クローズになっている可能性がある)。 CLOSEを送ると、その接続は送信側のエンド・ポイントに対してペンディング・クローズ状態になります。 したがって、接続の他端からCLOSEまたはCLOSEACKを受け取ってからでないと、再度接続を開くことはできません。
CLOSEオペレーションの受信は、接続の他端がその接続を閉じたことを意味し、受信側にとっても接続は閉じられます。 受信側エンド・ポイントは、この接続に対してこれ以上の(再度オープンされないかぎり)オペレーションを行う必要はありませんが、実装の入力バッファにたまっているデータは、接続のユーザーへ渡さなければなりません。 接続がペンディング・クローズではなく前もってオープンされているならば、受信側エンド・ポイントはその接続に対してCLOSEACKで応答する必要があります。
CLOSEACKオペレーションのレコード形式を次に示します。
| サイズ(バイト) | name | description |
|---|---|---|
| 1 | opcode | オペレーション・コード(OPEN) |
| 2 | ID | 接続識別子 |
エンド・ポイントは、受信側エンド・ポイントからのCLOSEを認識したことを知らせるためにCLOSEACKオペレーションを送ります。 IDが指定する接続の受信側が、このオペレーションを受信したときにペンディング・クローズの状態になっていない場合は、プロトコル違反になります。
CLOSEACKオペレーションの受信は、その接続がペンディング・クローズからクローズへ状態変化したことを示します。したがって、これ以後はその接続を再オープンできます。
REQUESTオペレーションのレコード形式を次に示します。
| サイズ(バイト) | name | description |
|---|---|---|
| 1 | opcode | オペレーション・コード(OPEN) |
| 2 | ID | 接続識別子 |
| 4 | count | 要求された追加バイト数 |
エンド・ポイントは、REQUESTオペレーションを送ることによって、その接続の入力要求カウントを増加させます。 IDが指定する接続が、送信側のエンド・ポイントから見てオープンされていない場合は、プロトコル違反になります。 エンド・ポイントの入力要求カウントは、countが示す値だけ増加します。 countは、符号付きの32ビット整数であり、マイナスまたはゼロの場合はプロトコル違反です。
REQUESTオペレーションの受信は、その接続の出力要求カウントをcountが示す値だけ増加させます。 受信側にとってその接続がペンディング・クローズ状態であるときは、いかなるREQUESTオペレーションも無視されます。
TRANSMITオペレーションのレコード形式を次に示します。
| サイズ(バイト) | name | description |
|---|---|---|
| 1 | opcode | オペレーション・コード(OPEN) |
| 2 | ID | 接続識別子 |
| 4 | count | 伝送バイト数 |
| count | data | 伝送データ |
エンド・ポイントは、TRANSMITオペレーションにより、指定の接続を介して実際のデータ伝送を行います。 IDが指定する接続が、送信側のエンド・ポイントから見てオープンされていない場合は、プロトコル違反になります。 エンド・ポイントの出力要求カウントは、countの値だけ減少します。 countは、符号付きの32ビット整数であり、マイナスまたはゼロの場合はプロトコル違反です。 また、TRANSMITオペレーションで送信側のエンド・ポイントの出力要求カウントがマイナスになるのも、プロトコル違反です。
TRANSMITオペレーションの受信は、countバイトのデータがキューに追加され、接続から読める状態になったことを示します。 受信側エンド・ポイントの入力要求カウントはcountの値だけ減少します。 この結果、入力要求カウントがゼロになり、かつ接続のユーザーがさらにデータを読み込む必要がある場合は、エンド・ポイントは追加のREQUESTオペレーションで応答しなければなりません。 接続が受信側のエンド・ポイントから見てペンディング・クローズならば、いかなるTRANSMITオペレーションも無視されます。
上述したプロトコル違反が発生した場合、または具象接続で通信エラーが発生した場合は、多重接続はシャットダウンされます。 実際の接続が終了し、すべての仮想接続も即時に閉じられます。 仮想接続上ですでに読込み可能になっていたデータは接続のユーザーによって読みとり可能です。
Copyright © 1997, 2017, Oracle and/or its affiliates. All rights reserved.
目次|前|次