エージェント間通信

佐藤一郎 1997/11/4

このモーバイルエージェントシステムでは、エージェントが別のエージェントの変数やメソッドを直接呼び出すことはできないようになっています。これは他のエージェントによる(不正な)呼び出しから守るためです。エージェント間で情報を交換する必要があるときには次の通信用のプリミティブを利用して通信をして下さい。また、これらのプリミティブを通じて通常のメソッド呼び出しと比較して、柔軟で並列性を活かした通信を実現することができます。通信の方式には、同期メソッド呼び出し、非同期メソッド呼び出し、一方向の非同期メッセージ送信の3種類があります。

エージェントコンテキストのAPIによる通信

エージェントコンテキストのAPIを利用することによりエージェント間通信を実現することができます。通信には、返値の有無、同期の有無により3種類があります。

エージェントコンテキストのAPI: エージェント間通信メソッド

メソッド名

第一引数の型

第二引数の型

メソッド返値の型

send()

AgentIdentifier

Message

void

call()

AgentIdentifier

Message

Object

future()

AgentIdentifier

Message

Future

メッセージクラス

Messageクラスはエージェント間通信のメッセージを規定するクラスです。 Messageクラスのインスタンスを下記のように作成し、引数に送信するメッセージの名前を与えます。メッセージの名前は文字列型の定数でなければなりません。また、大小文字を区別します。

Message(String name);

メッセージの引数は setArg() により設定します。

void setArg(Object obj);

ここで setArg() の引数が通信メッセージの引数となります。引数は0個以上を設定することができ、さらに型に制限はありません。ただし、 引数にはいるオブジェクトはJava のCore API に含まれるクラスからのインスタンスでなければいけません。

受信側では、メッセージ名と一致するメソッドに対して、setArg() で設定した順番の引数列として渡されます。このため、受信側のエージェントに送信メッセージと同じ名前のメソッドがあり、さらにそれの引数の数とそれらの型がその順番を含めて一致する必要があります。


Message msg = new Message("greeting");
msg.setArg("Hello");
msg.setArg("World");

この例は、greeting という名前のメッセージを生成するもので、その引数は、文字列型定数の "Hello" と "World" となります。なお、メソッド名がない場合や引数が一致しない場合は、ランタイムエラーとなります。現在は受信側で生じたランタイムエラーは送信側に伝わりません。このことは、同期的呼び出しをしたときに、受信エラーをおこすと送信側がデッドロックすることを意味する。今後のバージョンでは非同期メッセージ通信以外は受信側エラーを送信側に伝えるようにする予定です。

次に、メッセンジャークラスのインスタンスの送信メソッドを実行します。送信メソッドには、返値の有無、同期の有無により3種類があります。

エージェントコンテキストのAPIによる通信

Messageクラスを利用しない簡易通信メッソドが以下のように用意されています。これらはMessageクラスのインスタンスの生成などをしなくても送信できますが、引数の数に制限があります。

void send(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3, Object obj4)
void send(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3)
void send(AgentIdentifier aid, String name, Object obj1, Object obj2)
void send(AgentIdentifier aid, String name, Object obj1) 
void send(AgentIdentifier aid, String name)
 
void send(String name, Object obj1, Object obj2, Object obj3, Object obj4)
void send(String name, Object obj1, Object obj2, Object obj3)
void send(String name, Object obj1, Object obj2)
void send(String name, Object obj1)
void send(String name)
 
Object call(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3, Object obj4)
Object call(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3)
Object call(AgentIdentifier aid, String name, Object obj1, Object obj2)
Object call(AgentIdentifier aid, String name, Object obj1)
Object call(AgentIdentifier aid, String name)
 
Future future(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3, Object obj4)
Future future(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3)
Future future(AgentIdentifier aid, String name, Object obj1, Object obj2)
Future future(AgentIdentifier aid, String name, Object obj1)
Future future(AgentIdentifier aid, String name)

引数の数が多いときはMessageクラスを利用して通信をするようにしてください。なお、上記の簡易版はエージェントコンテキスト内部でMessageインスタンスを作成しているので通信コスト的には同じです。

非同期メッセージ送信

void send(AgentIdentifier aid, Message msg)

エージェント識別子aid、メッセージmsg

識別子aidをもつエージェントにMessageクラスのインスタンスmsgを送信します。メソッドの返値を受け取ることはできませんが、送信側エージェントは受信側エージェントの状態に関わらず処理を継続することができます。msgにはメッセージ名、引数を設定していく必要があります。

ただし、送信した時点で、受信側エージェント自体及び対応するメソッドが存在しない場合、そして引数の数や型が一致しない場合でもエラーを返すことはありません。また、受信側エージェントの結果を受け取ることはできません。

Message msg = new Message("greeting");
msg.setArg("Hello");
AgentContext ac = getAgentContext();
ac.send(aid, msg);

次に受信側エージェントにおいて呼び出されるメソッドの例をあげます。呼び出されるメソッドはその引数の数と型が対応している必要があります。

public void greeting(String s) {
	System.out.println(s+" World");
}
これを実行すると受信側エージェントにより "Hello World" が出力されます。

なお、Messageクラスによるメッセージの組立を行わないで送信するためのマクロが用意されています。これには引数の数により幾つか種類があります。

void send(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3, Object obj4)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

void send(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3)

エージェント識別子aid、メッセージ名name、引数obj1〜obj3

void send(AgentIdentifier aid, String name, Object obj1, Object obj2)

エージェント識別子aid、メッセージ名name、引数obj1, obj2

void send(AgentIdentifier aid, String name, Object obj1)

エージェント識別子aid、メッセージ名name、引数obj1

void send(AgentIdentifier aid, String name)

エージェント識別子aid、メッセージ名name

送信先のエージェント識別子がないときは自分自身に送信します。

void send(String name, Object obj1, Object obj2, Object obj3, Object obj4)
 

メッセージ名name、引数obj1〜obj4

void send(String name, Object obj1, Object obj2, Object obj3)

メッセージ名name、引数obj1〜obj3

void send(String name, Object obj1, Object obj2)

メッセージ名name、引数obj1, obj2

void send(String name, Object obj1)

メッセージ名name、引数obj1

void send(String name)

メッセージ名name

下の例では、まずエージェントコンテキスト ac を取得し、識別子 aid をもつエージェントに名前が"greeting"で、引数が文字型定数"Hello"となる メッセージを送り、続く処理を行うことができます。

AgentContext ac = getAgentContext();
ac.send(aid, "greeting", "hello");
 

次に受信側エージェントにおいて呼び出されるメソッドの例をあげます。呼び出されるメソッドはその引数の数と型が対応している必要があります。

public void greeting(String s) {
	System.out.println(s+" World");
}

上記を呼び出すことにより、"Hello World" を画面上に表示します。

同期メッソド呼び出し

void send(AgentIdentifier aid, Message msg)

エージェント識別子aid、メッセージmsg

識別子aidをもつエージェントにMessageクラスのインスタンスmsgで与えられたメッソドを呼び出します。返値はメソッドの返値となります。このとき、送信側エージェントは受信側エージェントが結果を返すまでブロックされます。

ただし、送信した時点で、受信側エージェント自体及び対応するメソッドが存在しない場合、そして引数の数や型が一致しない場合はエラーになります。また、受信側エージェントが対応するメソッドを実行しない場合はブロックすることになりますので注意して下さいを返すことはありません。

Message msg = new Message("greeting");
msg.setArg("Hello");
AgentContext ac = getAgentContext();
String str = ac.call(aid, msg);

上の例では、まずエージェントコンテキスト ac を取得し、識別子 aid をもつエージェントに名前が"greeting"で、引数が文字型となるメッソドを呼び出します。メソッドの実行結果は String 型の 変数 str に代入されます。次に受信側エージェントにおいて呼び出されるメソッドの例をあげます。呼び出されるメソッドはその引数の数と型が対応している必要があります。

public String greeting(String s) {
	return s +" World";
}
これを実行すると受信側エージェントにより、str には "Hello World" が入ります。

なお、Messageクラスによるメッセージの組立を行わないで送信するためのマクロが用意されています。これには引数の数により幾つか種類があります。これには引数の数により幾つか種類があります。

Object call(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3, Object obj4)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

Object call(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3)

エージェント識別子aid、メッセージ名name、引数obj1〜obj3

Object call(AgentIdentifier aid, String name, Object obj1, Object obj2)

エージェント識別子aid、メッセージ名name、引数obj, obj2

Object call(AgentIdentifier aid, String name, Object obj1)

エージェント識別子aid、メッセージ名name、引数obj1

Object call(AgentIdentifier aid, String name)

エージェント識別子aid、メッセージ名name

例を示します。

AgentContext ac = getAgentContext();
String s = ac.call(aid, "greeting, "Hello");
 

上の例では、まずエージェントコンテキスト ac を取得し、識別子 aid をもつエージェントに名前が"greeting"で、引数が文字型定数"Hello"となる メッセージを送り、受信側のメソッドを呼び出します。結果は String 型の 変数 str に代入されます。

また、受信側エージェントは以下のメソッドの型は以下のようになっている必要があります。

public String greeting(String s);

その例をあげます。

public String greeting(String s) { return s + " World"; }

上記を呼び出すことにより、str には "Hello World" が入ります。

非同期メッソド呼び出し(フュチャー通信)

void send(AgentIdentifier aid, Message msg)

エージェント識別子aid、メッセージmsg

識別子aidをもつエージェントにMessageクラスのインスタンスmsgで与えられたメッソドを呼び出します。返値はメソッドの返値となります。このとき受信側エージェントの状態に関わらずブロックせずに送信します。結果はフュチャークラス (Future) のインスタンスに格納されます。

そして、Future クラスのメソッド getReply() を通じてこのインスタンスを読み出すことにより得ることができます。取り出すときはこのメソッドを明示的にキャストする必要があります。

Object getReply()

ただし、フュチャーインスタンスを読み出した時点で、受信側エージェントが結果を返していないときは返えされるまでブロックされます。なお、送信した時点で受信側エージェント自体及び対応するメソッドが存在しない場合、または引数の数や型が一致しない場合でもエラーを返すことはありません。

Message msg = new Message("greeting");
msg.setArg("Hello");
AgentContext ac = getAgentContext();
Future f = ac.future(aid, msg);
/* do something */
String str = (String)f.getReply();
 

上の例では、識別子 aidをもつエージェントに名前greetingで、 引数が文字列型("Hello")のメッセンジャーを送信し、対応するメソッド("greeting(String s)")を呼び出します。結果はフュチャーオブジェクトに 送られますので、それをgetReply()により読み出し、それをString 型の 変数 str に代入します。

なお、Messageクラスによるメッセージの組立を行わないで送信するためのマクロが用意されています。これには引数の数により幾つか種類があります。これには引数の数により幾つか種類があります。

Future future(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3, Object obj4)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

Future future(AgentIdentifier aid, String name, Object obj1, Object obj2, Object obj3)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

Future future(AgentIdentifier aid, String name, Object obj1, Object obj2)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

Future future(AgentIdentifier aid, String name, Object obj1)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

Future future(AgentIdentifier aid, String name)

エージェント識別子aid、メッセージ名name、引数obj1〜obj4

例を示します。

AgentContext ac = getAgentContext();
Future f = ac.future("greeting", "hello");
/* do something */
String str = (String)f.getReply();
 
エージェント識別子がaidとなるエージェントの次のメソッドが呼び出されます。
public String greeting(String s) {
	return s + " World";
}

上記を呼び出すことにより、str には "Hello World" が入ります。

注意

エージェントに到着したメッセージはすべて到着順でメッセージキューに入れられます。このため、メッセージ送信や呼び出しをしてもすぐにそれの処理が開始されるとは限りません。また、メッセージを送信する前に存在していたエージェントでも、実際にメッセージが到着したときには、移動したり終了したりしている可能性があります。このため、通信失敗を考慮したプログラミング必須となります。

現在のランタイムシステムでは、同じランタイムシステム内のエージェント間の通信しかサポートしていません。このため、異なるコンピュータ上のエージェントには通信ができないことなりますが、別のコンピュータ上のエージェントと通信をしたい場合は、エージェントをそのコンピュータに移動させ、そのコンピュータ内のローカルエージェント間通信として実現できます。なお、リモートエージェントへの通信をサポートしないのは、引数に複雑なオブジェクトをもつ場合、その実行コストが上記のエージェント移動による実現と大差がないためです。このため、今後、値や文字列などの定数インスタンスに限定したリモート通信をサポートする可能性はあります。

通信内容に制限はありませんが、その引数にユーザが定義したクラスからのインスタンスを含めることはできません。これはセキュリティを維持するためです。通信内容はJavaのCore API に含まれるクラスからのインスタンスに限るべきです。

目次に戻る