
Aglet 抽象クラスは、モバイルエージェントの移動 (mobility) とライフサイクルをコントロールするために利用される基本的なクラス (dispatch(URL) 等) を定義します。 Aglet で定義された移動エージェントはすべて この抽象クラスを拡張する必要があります。 Aglet.dispatch(URL) プリミティブは aglet をローカルマシンから argument で指定された destination に移動させます。 Aglet.deactivate(long time) プリミティブは aglet を二次記憶装置に保存します。 Aglet.clone() プリミティブは、元の aglet と同じ状態を持つ aglet のインスタンスを生成します。 clone プリミティブが返すオブジェクトは Aglet オブジェクトではなく AgletProxy オブジェクトであることに注意してください。
Aglet クラスは aglet に関連付けられた属性にアクセスする場合にも利用されます。 Aglet.getAgletInfo() プリミティブによって取得される com.ibm.aglet.AgletInfo オブジェクトは、 aglet の到着時刻やカレントコンテキストの address 等のような動的な属性だけでなく、 aglet の生成時刻やコードベース等の aglet の継承属性を保有しています。
基本的なメソッドとそのセマンティクスを以下の表に示します。
| Method | Behavior |
| dispose() | aglet を処分する。 |
| dispatch(URL) | aglet を URL で指定された destination に発送する。 |
| deactivate(long duration) | 自分自身を persistent medium で保存するように aglet に指示する。 |
| getAgletInfo() | aglet の情報を取得する。 |
aglet インスタンスはすべて、その aglet のライフサイクルの中で変わることのないユニークな ID を持っています。 ID は、ユーザ ID やエージェントシステムのタイプ、なんらかの大きな数字等の複数の属性から成ります。 AgletID は、 aglet の表現の詳細のカプセル化に際してエージェントの一意の ID を保つためのオブジェクトです。
AgletProxy インタフェースオブジェクトは aglet のハンドルとして機能し、 aglet へのアクセスの一般的な方法を提供します。 aglet クラスは複数の public メソッドを持つため、セキュリティ上の問題から、他の aglet からのダイレクトなアクセスは避ける必要があります。他の aglet とコミュニケーションをとりたい場合にはまず proxy オブジェクトを取得し、その後このインタフェースを通して作用することになります。 言い換えると、 aglet proxy はエージェントを悪意のあるエージェントから守るための盾となるわけです。 proxy オブジェクトは呼び出しを受けると SecurityManager で呼び出したエージェントがそのメソッドの実行を許可されているかを調べます。 AgletProxy インタフェースのもう一つの重要な役割は、 location transparency を持つ aglet の提供です。 aglet がリモートのホストに駐在している場合、この aglet はリモートのホストに要求を転送しその返答をローカルホストに返します。
AgletProxy は以下の方法で取得可能です。
AgletContext クラスは aglet を占有する runtime 環境へのインタフェースを提供します。 aglet はすべて、 Aglet.getAgletContext() プリミティブを通してカレントの AgletContext オブジェクトへのリファレンスを取得しこれをホストのコンテキストのアドレスや AgletProxy の enumeration 等のローカル情報の取得に利用したりそのコンテキスト中で新しい aglet を生成したりすることが可能です。 aglet が一度発送されると、カレントに占有されていたコンテキストオブジェクトはこれ以上利用することができなくなり、到着時には destination コンテキストオブジェクトが代わりに添付されます。
runtime ライブラリは、このインタフェースの実装の提供について責任を持ちます。これにより、 aglet プログラマはこのインタフェースを実装する必要がなくなりました。
Aglet オブジェクトは Message クラスのオブジェクトを交換することでコミュニケーションをとります。 メッセージオブジェクトはそのメッセージの種別を示す String オブジェクトと任意のオブジェクトを argument として持ちます。 メッセージは Object AgletProxy.sendMessage(Message msg) 、 FutureReply AgletProxy.sendAsyncMessage(Message msg) 、void AgletProxy.sendOnewayMessage(Message msg) のどれかを呼び出すことで aglet に送付することができ、 argument として Aglet.handleMessage(Message msg) に渡されます。 詳細については section on messaging をご覧ください。
チケットオブジェクトは destination とトランスファの品質の両方を指定するために使われます。 言い換えれば、 aglet のトランスファの方法を定義するわけです。 チケットオブジェクトには destination、利用するプロトコル、タイムアウトや保証されるべき完全性 (integrity) やコンフィデンシャリティのレベル等のような品質情報を保持します。 このオブジェクトは、 destination を指定する方法として URL が使われる場合に利用されます。
FutureReply インタフェースのオブジェクトは非同期のメッセージ送信で返され、返答を受け取るための場所ホルダとして非同期に利用されます。 このインタフェースによって、受け手が返答が到着したかを判断し指定した時間内に返答が戻らない場合にも実行を続けることができるように指定したタイムアウト値まで返答を待つことが可能になります。
このメソッドは aglet のライフサイクルの中で一度だけ、その aglet が生成された時に呼ばれます。 Aglet API (dispatch(URL) 等) はコンストラクタ中に用意されていないので、 aglet プログラマは aglet の初期化のため onCreation(Object init) を使う必要があります。
このメソッドは dispose() メソッドが呼ばれた直後に呼ばれます。 aglet はそれまでアロケートされていたリソースをすべて放棄することになります。処分に応じて追加動作を実行することも可能です。
Aglet クラスの run() メソッドはインスタンスが生成された時および回復された時に毎回呼ばれます。 このメソッドはコンテキストを占有する時に毎回呼ばれるため、共通のタスクを定義するのに適した場所です。
aglet に送られたメッセージはすべて handleMessage メソッドに渡されます。 aglet プログラマは、入ってくるメッセージが既知のメッセージかを調べ、そのメッセージの種別に応じたタスクを実行することが可能です。
public class HelloAglet extends Aglet {
public void onCreation(Object init) {
System.out.println("created!");
}
public void run() {
System.out.println("hello!");
}
public boolean handleMessage(Message msg) {
if (msg.sameKind("sayHelloAgain") {
System.out.println("hello!");
return true;
}
return false;
}
public void onDisposing() {
System.out.println("bye!");
}
}
aglet オブジェクトは一度生成されると、リモートのサーバに発送されたり、リモートのサーバから撤収されたり、非活性化されてセカンドストレージに移されたりした後、活性化されます。

例: atp://aglets.ibm.com:1434/context_name発送処理によって、 aglet は実行を中止してその内部状態とバイトコードをスタンダードフォームでシリアライズし、 destination へ移送させられます。 受け手側では、 受け取ったデータに応じて Java オブジェクトが再構築され、新しいスレッドがアサインされて実行されます。
aglet は永続的に動くことが可能です。 モバイル aglet は bit ストリーム中でシリアライズ可能である必要があるため、モバイル aglet はすべて本質的に永続が可能です。 Aglet.deactivate(long timeout) プリミティブは aglet をセカンドストレージに保管して、指定された時間 (ミリ秒単位)の間スリープを掛けます。 与えられた時間が過ぎるか、あるいは他のプログラムが活性化を要求するかすると aglet は非活性化されたのと同じコンテキスト中で活性化されます。
ガベージコレクタに自動的に解放される一般の Java オブジェクトと異なり、 aglet オブジェクトは活性化されているためごみとなるか否かを決定することができます。 aglet を kill するよう dispose() メソッドを呼べば、対象となる aglet の現在の状態に適した終了作業を実行するための onDisposing() が呼ばれます。 (これは、 Java でオブジェクトをガベージコレクションで回収させる finalizer() とは異なるものです。 ) ファイルデスクリプタや DB コネクション等アロケートしたリソースは自動的に解放されないため、 aglet プログラマには、リソースの解放を行なう責任があります。

import com.ibm.aglet.Aglet;
import com.ibm.aglet.event.MobilityEvent;
import com.ibm.aglet.event.MobilityListener;
class MyListener implements MobilityListener {
public void onDispatching(MobilityEvent l) {
closeWindow();
closeFile();
}
public void onReverting(MobilityEvent l) {
openWindow();
donNextJob();
}
public void onArrival(MobilityEvent l) {
}
}
public class MyAglet extends Aglet {
public void onCreation(Object init) {
MobilityListener listener = new MyListener();
addMobilityListener(listener);
}
}
MyListener オブジェクトの onDispatching() メソッドは aglet が実際に発送される前に、 onArrival() メソッドは aglet が destination に到着した後に呼ばれます。 aglet が複数の listener を持つ場合には、添付された順に呼び出されます。 このようにして、 aglet プログラマは後に続くイベントに応じて実行される onDispatching のような action を、 aglet がいつ誰に発送されたかに関係なく実装することができます。
| 状況 | イベントオブジェクト | Listener | 呼ばれるメソッド |
|---|---|---|---|
| 複製 (clone) を生成する直前 | CloneEvent | CloneListener | onCloning |
| 複製 (clone) が生成された時 | CloneEvent | CloneListener | onClone |
| 複製 (clone) が生成された後 | CloneEvent | CloneListener | onCloned |
| 発送の直前 | MobilityEvent | MobilityListener | onDispatching |
| 撤収の直前 | MobilityEvent | MobilityListener | onReverting |
| destination への到着後 | MobilityEvent | MobilityListener |
onArrival |
| 非活性化の直前 | PersistencyEvent | PersistencyListener | onDeactivating |
| 活性化した後 | PersistencyEvent | PersistencyListener | onActivation |
public class Updater extends MobilityAdapter {
AgletProxy _finder;
Message _update_msg = new Message("update");
public Updater(AgletProxy finder, String name) {
_finder = finder;
_update_msg.setArg("name", name);
}
public void onArrival(MobilityEvent ev) {
try {
_update_msg.setArg("proxy", ev.getAgletProxy());
_finder.sendMessage(_update_msg);
} catch (Exception ex) {
}
}
public void removeFromRegistry() {
try {
Message remove = new Message("remove");
remove.setArg("name", _update_msg.getArg("name"));
_finder.sendMessage( remove );
} catch (Exception ex) {
}
}
}
public class Registry extends Aglet {
Hashtable _registry = new Hashtable();
public boolean handleMessage(msg msg) {
if (msg.sameKind("Lookup") {
msg.sendReply(_registry.get(msg.getArg("name")));
} else if (msg.sameKind("Update") {
_registry.put(msg.getArg("name"), msg.getArg("proxy"));
} else if (msg.sameKind("Remove") {
_registry.remove(msg.getArg("name"));
} else return false;
return true;
}
}
これらの listener オブジェクトはシーケンシャルに呼ばれるということをプログラマは心に留めておく必要があります。
このため、後から追加された listener オブジェクトは、それ以前の listener の振る舞いによっては呼ばれないことがあります。
例えば、 listener オブジェクトが onArrival で対象となっている aglet を消滅させた場合、それ以降の listener は呼ばれることはありません。
class ListingAglet extends Aglet {
// result and its contents are transferred.
private Vector result = new Vector();
transient InputStream in = new InputStream(); // will not be transferred
}
シリアライズ化および非シリアライズ化のプロセスにおいて、その状態の一部であるオブジェクトは普通値に依って移動します。 この結果の一つとして、複数の aglet に共有されるオブジェクトは一度シリアライズ化されると、複製され、発送、複製、非活性化、活性化を施された後共有されなくなります。
一方、 aglet の proxy はトランスファされる時に aglet の ID とアドレスを保持し、元の aglet への正しい参照を回復するためにこの情報を使います。 そのため AgletProxy オブジェクトは、 aglet が同じ location に駐在している間だけでなく、 proxy がリモートのホストにトランスファされたり非活性化されたりした場合でも正しい aglet への参照を保つことができます。
class Transferrable implements java.io.Serializable {
Hashtable hash; // Hashtable is also serializable
}
class NotTransferrable {
int dummy;
}
class MyClass extends Aglet {
transient FileDescriptor fd; // never get transferred.
int value; // moved by value
String str; // moved by value
Object transferable = new Transferrable(); // move by value
Object not_transferable = new NonTransferable(); // throws NotSerializableException.
AgletProxy proxy; // moved by reference
}
クラス変数はオブジェクトの一部ではないので、クラス変数の値がシリアライズされることはありません。 従って、クラス変数は各クラスがローカルに持つものであり、aglet は新しい destination に到着すると異なった値を持つ可能性があります。
public class MyAglet {
static int class_variable = 0;
public void onCreation(Object init) {
class_variable = 10;
dispatch("atp://next.place");
}
public void run() {
if (class_variable != 10) {
System.out.println("Class variable never get transferred!");
}
}
}
RMI のリモートインタフェースは、リモートから呼び出し可能なメソッドを持つリモートオブジェクトを定義するために利用されます。 クライアント側の RMI オブジェクト (stub) は更新を必要とせずに Aglets ライブラリと仕事をすることができます。 aglet がリモートオブジェクトと共に発送された場合、リモートオブジェクトは aglet が整列から解放された時に自動的にサーバオブジェクトに戻ります。
しかし、 Aglets はサーバオブジェクトを取り扱うことはできません。 RMI のサーバオブジェクトのトランスファを行なうと、 destination サイトでこのサーバオブジェクトの複製を生成することになります。
public class MyAglet extends Aglet {
RMIHelloImpl impl = new RMIHelloImpl();
String naming = "//naming.com/hello";
public void onCreation(Object init) {
java.rmi.Naming.rebind(naming, impl);
addMobilityListener(new MobilityAdapter() {
void onArrival(MobilityEvent ev) {
java.rmi.Naming.rebind(naming, impl);
impl.hello(); // calling local method
// It cannot be Stub anyway because it's impl!
}
});
}
}
これは、 JDK1.1 での RMI の制限です。
(JDK1.2 ではリモートオブジェクトの「 unexporting 」をサポートしており、この問題は解決されました。)
シリアライズ可能なオブジェクトについて writeObject/readObject メソッドをカスタマイズしようとする Aglet プログラマは、 writeObject() が aglet をサスペンドした後に呼ばれるのと違って onDispatching() は aglet 実行中に呼ばれることに注意する必要があります。 同様に readObject() は aglet が整列から解放されている間に呼ばれるため、 getAgletContext() のような aglet プリミティブは動作しません。 逆に、 onArrival() は aglet 全体の回復および活性化に成功した後に呼ばれるため、 Aglet API を呼ぶことが可能です。
現在のところ、 AgletProxy は動きまわる aglet を完全に把握することができません。 aglet が発送されてしまうと、それまで aglet を参照していた proxy は無効になります。 参照を貯蔵するためのメカニズムは将来提供される予定です。
MyAglet extends Aglet {
public boolean handleMessage(Message msg) {
if (msg.sameKind("doJob")) {
doJob();
} else if (msg.sameKind("shutdown")) {
deactivate(0);
}
}
}
Aglets はメッセージ送信として以下のタイプをサポートしています。
String answer = proxy.sendMessage(new Message("question"));
System.out.println(answer);
FutureReply future =
proxy.sendAsyncMessage(new Message("question"));
int num_task = 10;
// do private job at most 10 times while waiting for the result.
while(future.isAvailable() == false && num_task-- >0) {
doPrivateJob();
}
System.out.println( (String)future.getReply() );
注:
aglet が自分自身にメッセージを送った場合、デッドロックを防ぐためそのメッセージはキューの最後尾ではなくキューの頭に置かれ即座に実行されます。
proxy.sendOnewayMessage(new Message("question"));
public boolean handleMessage(Message msg) {
if (msg.sameKind("sayHello")) {
System.out.println("Hello");
return true; // i know this message...
}
return false; // false, otherwise
}
false が返った場合には、メッセージの送り主は NotHandledException を受け取ることでこのメッセージが取り扱われなかったことを知ります。
oneway message が取り扱われたかを知る方法はありません。
Future future = proxy.sendAsyncMessage();
...
try {
Object reply = future.getReply();
} catch (NotHandledException ex) {
// the receiver didn't handled the message
} catch (MessageException ex) {
// an exception has been thrown in the receiver's handleMessage()
System.out.println(ex.getException());
}
Aglets でのメッセージングは、 acknowledge-タイプの返答もサポートしています。
このタイプを利用することで受け手はそのメッセージの取り扱いを完了するよりも前に返答 (結果) を送ることが可能です。
返り値がある場合には、その値を返すためにこのインタフェースを利用する必要があります。
一度 Message.sendReply() メソッドを経由して返答を送ると二度と返答を送ることはできず、 handleMessage の返り値は無視されます。
public boolean handleMessage(Message msg) {
if (msg.sameKind("accessDB")) {
openDB(); // pseudo code
Object reply = accessDB(msg.getArg());
msg.sendReply(reply);
closeDB();
return true; // i know this message...
}
return false; // false, otherwise
}
キュー
[A] -> {}: Aglet
[B] -> {[A]}: Aglet
[C] -> {[B][A]}: Aglet
{[C][B]}: Aglet ([A] 取り扱い中)
{[C]}: Aglet ([B] 取り扱い中)
{}: Aglet ([C] 取り扱い中)
メッセージには、 kind に応じたプライオリティを指定することができます。
プライオリティの高いメッセージは、プライオリティの低いメッセージよりも早く取り扱われます。
メッセージは並行して扱われるため、プライオリティは順序を保証しません。
あるメッセージが他のものよりも先に取り扱われる可能性を高くすることができるだけです。
今、キューの中にそれぞれ 3、 5、 6 というプライオリティを持つ 3 つのメッセージが入っているとし、さらに、プライオリティがそれぞれ 4 と 7 の 2 つのメッセージを非同期に送る場合を考えます。 先にプライオリティ 4 のメッセージを送った場合でも、プライオリティ 7 のメッセージの方が先に扱われます。
{[3][5][6]}: Aglet
[4] -> {[3][5][6]}: Aglet
[7] -> {[3][4][5]}: Aglet ([6] 取り扱い中)
{[3][4][5][7]}: Aglet ([6] 取り扱い継続中)
{[3][4][5]}: Aglet ([7] 取り扱い中)
Aglets はプライオリティとしてメッセージをキューに入れない NOT_QUEUED もサポートしています。
今プライオリティとして NOT_QUEUED を持つメッセージ [D] が送られてきたとすると、このメッセージは aglet に直接渡されて即座に並列実行されることになります。
[A] -> {}: Aglet
[B] -> {[A]}: Aglet
[D] -> {[B][A]}: Aglet
{[B]}: Aglet ([A] 取り扱い中) ([D] 取り扱い中)
{}: Aglet ([B] 取り扱い中) ([D] 取り扱い中)
{}: Aglet ([D] 取り扱い中)
メッセージのプライオリティは MessageManager.setPriority(String, int) プリミティブで設定することができ、エージェントの MessageManager オブジェクトは Aglet.getMessageManager() プリミティブ経由で取得することができます。
他のメッセージを扱っている他のスレッドが Aglet.notifyMessage() を呼んで待ちに入っているスレッドに再開の指示を出した場合、このスレッドは目覚めて実行を再開します。 待ちに入ったスレッドは、再開の指示が出なければ、止められるまで永遠に待ち続けます。 それに対して、待ち時間の最大値 timeout をミリ秒単位で指定してその時間が過ぎた後目覚める waitMessage(long timeout) も利用できます。
キュー 待ち
{[D][C]}: Aglet {[A][B]}
{[D]}: Aglet {[A][B]} ([C] 取り扱い、notifyMessage() 呼び出し)
{[D][C]}: Aglet {[B]} ([A] 取り扱い)
Aglet.notifyAllMessages() は、待ちに入っているスレッドすべてを順番に起こします。
起こされたスレッドはメッセージキューの最初に置かれ、一番振るい待ちスレッドが即座に再開されます。
caller スレッドは起こされたスレッドの次の位置に置かれ、再開されたスレッドが完了した後に再開されます。
キュー 待ち
{[D][C]}: Aglet {[A][B]}
{[D]}: Aglet {[A][B]} ([C] 取り扱い、 notifyAllMessages() 呼び出し)
{[D][C][B]}: Aglet ([A] 取り扱い)
キュー
{[C][B]}: Aglet ([A]* 取り扱い、exitMonitor() 呼び出し)
{[C]}: Aglet ([B]* 取り扱い) ([A] 取り扱い)
注: * はモニタの所有者を示します。
この API を利用することで、メッセージを受け取っている間のバックグラウンドタスクを実装することが可能です。
public void SiteWatcherAglet extends Aglet {
public void run() {
exitMonitor(); // now the next message would start.
while( condition == true) {
watchSite("http://home/index.html");
}
}
}
public StackAglet extends Aglets {
static int capacity = 10;
Object stack[] = new Object[capacity];
int num = 0;
public handleMessage(Message msg) {
if (msg.sameKind("push")) {
push(msg);
} else if (msg.sameKind("pop")) {
pop(msg);
} else if (msg.sameKind("isFull")) {
msg.sendReply( num == capacity);
} else if (msg.sameKind("isEmpty")) {
msg.sendReply( num == 0 );
} else return false;
return true;
}
private void push(Message msg) {
while (num == capacity) {
waitMessage();
}
stack[num++] = msg.getArg();
if (num==1) {
notifyMessage(); // wake up "pop" message
}
}
private void pop(Message msg) {
while(num==0) {
waitMessage();
}
msg.sendReply(stack[--num]);
if (num == (capacity -1)) {
notifyMessage(); // wake up "push" message
}
}
}
Aglets のアーキテクチャは 2 つのレイヤから成っており、それぞれの関数にアクセスするためのインタフェースを定義する 2 つの API を備えています。

The Aglets runtime itself has no communication mechanism for transferring the serialized data of an aglet to destinations. Instead, the Aglets runtime uses the communication API that abstracts the communication between agent systems. This API defines methods for creating and transferring agents, tracking agents, and managing agents in an agent-system- and protocol-independent way.
The current Aglets uses the Agent Transfer Protocol (ATP) as the default implementation of the communication layer. ATP is modeled on the HTTP protocol, and is an application-level protocol for transmission of mobile agents. To enable remote communication between agents, ATP also supports message-passing.
// MAF IDL
module MAF {
.....
struct Name {
Authority authority;
Identity identity;
AgentSystemType agent_system_type;
};
....
interface MAFAgentSystem {
// Agent creation and transfer
Name create_agent(in Name agent_name, ...) raises (..);
void receive_agent(in Name agent_name,
in AgentProfile agent_profile,
in octet_string agent, ...) raises (..);
// Agent management
void get_agent_status(in Name agent_name) raises (..);
void list_all_agents(in Name agent_name) raises (..);
void suspend_agent(in Name agent_name) raises (..);
void resume_agent(in Name agent_name) raises (..);
void terminate_agent(in Name agent_name) raises (..);
// etc...
}
}
Although MASIF interfaces are intended for CORBA objects, the interfaces
actually defined in Aglets are not CORBA-based. In fact, they are
defined as normal Java classes, interfaces or abstract classes that act as
common wrappers for the protocols actually being used. Thus, it is possible
and easy to use various kinds of protocol other than CORBA/IIOP
such as ATP and RMI.
We chose this approach because relying on the specific transport protocol or the specific transport mechanisms has at least two disadvantages. First, it would be technically inadequate to require mobile agent systems to use a specific protocol unless it became pervasive and widely supported. One of the benefits of mobile agents is in their ability to hide the existence of network communication. Second, some Java environments such as PersonalJava have neither RMI nor CORBA as their core API. Therefore, it would be more practically desirable if the runtime could choose the communication mechanisms in accordance with its system requirements.
The following figure shows the architecture of the communication layer. com.ibm.maf.MAFAgentSystem is an abstract class that defines a set of methods equivalent to the MASIF interface. There are two kinds of class that extend this abstract class. One is an implementation class that provides the agent system facility, and the other is a stub object that transfers a request to a destination.

On the other hand, an aglet server has an implementation of
MAFAgentSystem that actually handles the requests. It is the
agent-system provider's responsibility
to provide the implementation of MAFAgentSystem. Aglets has the com.ibm.aglets.MAFAgentSystem_AgletsImpl
class as an implementation. Furthermore, a server has one or more daemons
to accept requests from a sender. A server may support multiple protocols
by having multiple daemons to handle each protocol. When a daemon accepts
requests, it then forward these requests to the MAFAgentSytem_AgletsImpl.
Note:
CORBA-based transport layer will be provided in a future release
of Aglets.

An AgletRef object is an internal representation of an aglet object. It has all necessary components for the aglet a MessageManager to control incoming messages, and a ResourceManager to manage resources consumed by the aglet and to manage its related resources such as security information and the AgletInfo object. It is also the implementation of the abstract methods defined in Aglet class. It implements most of the functionality to be provided by the com.ibm.aglet.Aglet class. The AgletRef object is what the aglets framework deals with. The latter has a reference table to store the mapping from the AgletID to the actual aglet instance. An AgletRef object is created and inserted into this table when an aglet is created or arrives, and removed from it when the aglet is dispatched or disposed of.
An aglet has a MessageManager object, which governs all incoming messages sent to the aglet. The MessageManager manages and controls the order and concurrency of these messages. See the Messaging section for more details.
An aglet uses local resources such as files or threads to carry out its tasks during its lifetime. It may open a dialog window to interact with users, or create a new thread to perform some concurrent task.
These resources are managed by a ResourceManager object, which is allocated for each aglet. Currently all threads and windows created by the aglet are managed by this ResourceManager. When an aglet is dispatched, deactivated, or disposed of, these two resources are immediately stopped and disposed of. Other kinds of resources such as file and socket, however, are not captured by this manager. It is left to the garbage collector to decide whether or not such resources will be released, and if so when. Therefore, the programmer must release them manually to ensure their immediate release.
Unlike normal Java objects, aglets are never garbage-collected (GC) automatically, because an aglet is active and has its own threads of control. An aglet programmer needs to explicitly dispose of an aglet.
When an aglet has been dispatched, deactivated, or disposed of, the AgletRef object is removed from the reference table. In addition, the internal reference to that aglet and associated components such as MessageManager object or properties are set to null so that the garbage collector can sweep up these dangling objects.
This means that if you have a live reference to this aglet elsewhere, it will not be GCed. For example, if you have a reference to the static variable of the class, it will not be GCed.
public MyAglet extends Aglet {
static MyAglet aglet = null; // the previous one may be GCed.
public void onCreation(Object init) {
aglet = this; // keep the reference
}
}
In mobile agent systems, classes for an agent need to be available at the server on which the agent is running. The class of an agent needs to be available at the server at the time of creation, and to be available at the destination to which it moves. Therefore, a mobile agent system needs to have a facility for loading bytecode on demand, or for transferring the bytecode along with the agent.
Aglets supports two schemes for transferring bytecode more efficiently ways, and also make use of a cache to reduce unnecessary downloading of classes. It is important for aglet programmers to understand class mobility, how a class is loaded, and when a class is transferred. The rules of class mobility are explained in the following sections:
Java has a special class, called ClassLoader, that is capable of defining a new class from bytecode. Once a class has been defined by the class loader, all requests for new classes within that class are handled by that class loader. Aglets has a dedicated subclass of ClassLoader, called AgletClassLoader. Each aglet is associated with exactly one class loader, and all classes required by the aglet are managed by that class loader. (Note that one class loader may manage multiple aglet instances.) Suppose there is an aglet
Ex.1
class MyAglet extends Aglets {
MyDialog dialog = null;
public void onCreation() {
dialog = new MyDialog();
}
}
and that both MyAglet and MyDialog classes are placed in the codebase below:
atp://aglets.codeserver.com/publicIn this example, both MyAglet and the MyDialog classes are managed by one AgletClassLoader.
There may be several sources of the bytecode of a given class. It is therefore worth understanding which class definition is actually chosen by the class loader and used. AgletClassLoader maintains a cache table for classes formerly defined by the class loader, and it first looks up a definition in that cache table. If a definition is not found in the cache , it asks the system class loader to load it from CLASSPATH (for security). If this is not successful either, it defines a new class by extracting bytecode from CacheManager if this is available, or by loading from the codebase otherwise. Once the new class has been defined, it is cached in the cache table and will be reused by other aglets later.
Ex.2
class MyAglet2 extends Aglets {
MyDialog dialog = null;
String str = "Hi";
// LocalData is installed on the CLASSPATH
LocalData data = new LocalData();
public void onCreation(Object init) {
dialog = new MyDialog();
}
}
If MyAglet2 is loaded by the same classloader of MyAglet, the cached
class is used to resolve the MyDialog class. If it is loaded by a different
classloader, it will use the bytecode in the CacheManager to define a new
MyDialog class in the class loader. Since both the java.lang.String
class and the LocalData class are locally available on the CLASSPATH,
they will be loaded by the system class loader and then become system classes.
As mentioned before, there are two possible ways of bringing bytecode to the server. The first is to download and define a class on demand after an aglet moves, and the second is to transfer bytecode as an agent moves. Aglets supports these two schemes, and in some case uses a combination of the two. This subsection illustrates the rules according to which classes are transferred.
Aglets classifies Java classes into four categories, according to where they came from, and manages them in different ways:
If no archive is specified, only classes loaded from the codebase are transferable along with the aglet. By this we mean that all classes located in the aglet's codebase can be transferred when it moves except when a class with the same name exists on the CLASSPATH. Such system classes are never transferred. In that of Ex.2, the bytecode of the LocalData class would never be transferred while that of MyDialog might be. The set of classes to be transferred are determined at runtime in the serialization process. That is, the classes of all objects visited during the serialization are collected and transferred.
Note that the class of a reference with null value is never transferred. So what happens if such a reference exists and the class is used after the aglet is dispatched? Let's take a look at an example:
Ex.3
class MyAglet extends Aglet {
MyDialog dialog = null;
public void onCreation(Object init) {
addMobilityListener(new MobilityAdapter() {
public void onArrival(MobilityEvent ev) {
// create MyDialog after arriving at the destination.
dialog = new MyDialog();
}
});
dispatch(....);
}
}
Since the reference "dialog" is null, the MyDialog class is not transmitted
when the aglet is dispatched. Instead, the aglet is sent without the MyDialog
class and then searches for the MyDialog class after it arrives. Because it
tries to load the class by following the class loading rule explained above,
it may load the class definition from its codebase, or it may use the cached
class, if any exists.
If you want to make sure that such classes are also transferred, you may include the class instance as its state, like this:
Ex.4
class MyAglet extends Aglet {
MyDialog dialog = null;
Class class_used_later = MyDialog.class;
}
This forces the system to transmit MyDialog along with the aglet.
Finally, if an aglet attempts to transfer an object whose class loader is different from the aglet's class loader, it fails with a SecurityException. This is because Aglets does not allow an aglet either to load a class from two different codebases or to extract the bytecode from other aglets, for security reasons.
Since ClassLoader cannot define two classes with the same name, the old class already stored in the cache may be used even if the updated class has been sent. This would cause a problem in deserializing objects because the serialized data might not compatible with it. Even worse, the class will behave differently.
Ex.5
// old class
class MyAglet extends Aglet {
MyDialog dialog = new MyDialog();
}
// revised class
class MyAglet extends Aglet {
MyDialog dialog = new MyDialog();
public void onCreation(Object init) {
dialog.show(); // I forgot to show the dialog..
}
}
# MyAglet must be used at the destination
Even if you fix the source code (Ex.5), you may get the same result as
before because it might be cached. Therefore, a class definition must be
forcibly updated as it evolves, while the cost of transmitting and defining
new classes must be reduced. The Aglets solves this problem by allocating
different class loaders for different sets of classes. In the following
section, we describe how a class loader is chosen and when one is newly created.
To avoid the version conflict problem, Aglets sends the information on the version of a class as well as its name along with the class definition. AgletClassLoader also maintains this information in the cache. This version information is taken from the MANIFEST file in a JAR archive or computed at runtime.
With this information, the receiver can find out what classes are used within the aglet even before receiving all the bytecode. Thus, it can determine the class loader that matches the information in the "ClassLoader cache". Note that only classes sent along with the aglet are evaluated in searching the class loader. If an aglet requires another class that has not appeared in this version table since the aglet arrives (MyDialog in Ex.3), the same problem may occur.
Note that Aglets does not support the automatic version detection of aglets created from plain codebase (without JAR). You may therefore get an old version of classes. We recommend you to use a JAR archive if you want to make sure that the correct version is used for creation.
The current Aglets, however, sends as much bytecode as possible, because the above scheme has several drawbacks and the *best* scheme is not yet established. For example,

Furthermore, it is impossible for an aglet to dispatch itself back into the intranet through the firewall. To get back inside a firewall, an aglet has a unique primitive, called retract, which lets a client "pull" the dispatched aglet from a remote site. This allows you to dispatch an aglet outside the firewall and get it back into the intranet.
Aglets uses an organizational approach whereby all agent systems in a certain domain are deemed trustworthy, and evaluates the authenticity of the agent depending on the domain in which it has been roaming around. A user first authenticates himself/herself to the system, and the system then issues the credentials of the user's agent. The agent system then evaluates the authenticity of the credentials, to determine whether or not they were issued within the same domain. It may downgrade the authenticity or simply deny access, depending on conditions such as where the agent has traveled and so forth. Host authentication is used to identify the domain to which the communicating host belongs.
Although the current Aglets does not fully support these services because of the limited support for encryption in JDK, it does provide a reasonable level of security to make it safe to use mobile agent applications. The following security features are supported in the latest Aglets runtime:
Aglet servers are able to authenticate whether the contacting server belongs to a certain domain. All servers that belongs to one domain share a secret key, and can authenticate the contacting server by means of that secret key using MAC (Message Authentication Code: a secure hash value computed from a content and nonce value). The advantage of this is that MAC does not have to be signed by means of encryption algorithms, and it can simply be implemented on top of vanilla JDK.
After the authentication between servers has been established, the credentials of the aglet are sent along with the aglet. The receiver will then decide how much it trusts the credentials sent by the server on the basis of the information obtained by the host authentication. In Aglets, the server simply trusts the credentials if they were sent from the server in the same domain.
To use the domain authentication, each user (or server administrator) needs to obtain the secret key of the domain from the domain authority. The domain authority is responsible for generating a domain key for a specific server. The shared secret key is signed with the user's password, and thus the user is required to give the correct user id and password to make it effective. This key file must be kept secret, because it is not encrypted.
One disadvantage is that it cannot identify and verify the communicating
hosts. Once the shared key is stolen from a server in the domain,
there is no way of distinguishing valid servers and the server from which the
key was stolen. As a result, all servers in the same domain are exposed
to dangers.
java.io.FilePermission : File read/write/execute java.net.SocketPermission : Socket open/connect/listen/accept/multicast java.awt.AWTPermission : topLevelWindow, systemClipboard java.util.PropertyPermission : Java property java.lang.RuntimePermission : print.queueJob, load library java.lang.reflect.ReflectPermission : class field access com.ibm.aglets.security.ContextPermission : context property, start, shutdown com.ibm.aglets.security.AgletPermission : dispatch, deactivate, etc. com.ibm.aglets.security.MessagePermission : messaging
The principal name is the user name of the target aglet (e.g., "moshima", "guenter", "this", or "anonymous"), and the operation name is the method name of the Aglet class (e.g., "dispatch", "dispose");
The principal name is the user name of the target aglet (e.g., "moshima", "guenter", "this", or "anonymous"), and the kind of the message is the kind of message to be sent ("show", "getResult", etc.);
grant codeBase "http://trusted.com",
owned by "oshima"
{
permission java.io.FilePermission "C:\\temp", "read";
permission com.ibm.aglet.security.ContextPermission "property.*", "write";
permission com.ibm.aglet.security.AgletPermission "oshima", "dispose";
}
This policy file is created at $HOME/.aglets/security/aglets.policy
on an idividual user basis. A domain-based policy is not yet supported.
Once supported, the domain authority should be able to specify domain-wide
permissions.
Once code signatures are supported, "signedBy" will be available
as a means of specifying the manufacturer of an aglet.
When the box labeled "Enable to Open Windows" for an untrusted is checked, untrusted aglets are allowed to open windows.
With this configuration, an incoming aglet normally gains access to services via message passing. Then, it can leave for the next server along with the result obtained in the server, or send it to the home server by remote message passing and die. Message passing is also under the control of the security manager and a receiver aglet can also deny a request.
The server can be also configured so that it allows a limited set of aglets to access the local resources and services. In this case, for example, an aglet may open a direct connection to a database and issue a SQL request. This gives an aglet great flexibility in an using the server resources, while this also creates the risk that an aglet may misuse or abuse these resources, and eventually may cause the system to misbehave or even crash.
For example, a administration tool would provide comprehensive ways of creating service aglets, monitoring visiting aglets, and disposing of them if necessary. Furthermore, the viewer can provide service for an incoming aglet in response to its arrival. For example, a gateway server may offer an incoming aglet the itinerary object for a further trip inside the gateway when it arrives.
On the other hand, the viewer can be customized for users or specific services. For example, a desktop-like interface may use an icon for aglets and drag and drop operations to manipulate them for people who is familiar with PCs, while a kiosk for novice users will define a single-click, Web style interface.
The Aglets Server API allows developers to embed and bootstrap the aglets server in their applications, and to configure the server's SecurityManager, Persistence, Viewer, and so on.
Furthermore, an application may not want to accept incoming aglets while it is creating an aglet or dispatching and retracting it to obtain a service. The Server API allows an application to run the Aglets runtime library without daemon capability for this purpose.
For example, the console of a massive network management system may not have to install the server facility. The console application, which typically has a client capability, can create a monitor aglet on a machine, and let a detective aglet roam multiple machines and send information back to the console.
The ContextEvent class and ContextListener interface allow you to monitor the activities of aglets in the context and to take actions in response to these activities. The typical application of this API is "AgletViewer" which displays a list of aglets running in the context and lets you control them by, for example disposing of them or dispatching them.
The Aglets Server interface makes it possible to write an application capable of hosting, receiving, and dispatching aglets. With this interface, you can take advantage of mobile agent technology in your application without running an independent, separate server program such as Tahiti.
The other methods are for receiving notification of the activities of aglets. It is guaranteed that when an event occurs, the proxy of an aglet given as a parameter in the ContextEvent will still be available via the getAgletProxies() primitive. If an aglet is being disposed of, however, the instance of the aglet is in the middle of an invalidating process and may have an intermediate state. Therefore checking methods such as isValid(), may not return correct values.
package com.ibm.aglet.system;
public interface ContextListener {
public void contextStarted(ContextEvent ev);
public void contextShutdown(ContextEvent ev);
public void agletCreated(ContextEvent ev);
public void agletCloned(ContextEvent ev);
public void agletArrived(ContextEvent ev);
public void agletActivated(ContextEvent ev);
public void agletReverted(ContextEvent ev);
public void agletDisposed(ContextEvent ev);
public void agletDispatched(ContextEvent ev);
public void agletDeactivated(ContextEvent ev);
public void agletStateChanged(ContextEvent ev);
public void showDocument(ContextEvent ev);
public void showMessage(ContextEvent ev);
}
agletStateChanged() is called when an aglet changes its state
by, for example, calling the Aglet.setText(String) primitive.
showDocument()
is called when an aglet calls the Aglet.showDocument(URL) primitive
to request that a specified document be shown. It is left to the context
implementor to decide whether or how to implement the showDocument
method. The default implementation of Tahiti launches an external
Web browser, but another implementation may use an internal browser written
in Java to load and display an HTML document.
package com.ibm.aglet.system;
public class ContextEvent extends AgletEvent {
public static final int CONTEXT_FIRST = 1000;
public static final int CONTEXT_LAST = 1014;
public static final int STARTED = CONTEXT_FIRST; // 1000
public static final int SHUTDOWN = CONTEXT_FIRST + 1; // 1001
public static final int CREATED = CONTEXT_FIRST + 2; // 1002
public static final int CLONED = CONTEXT_FIRST + 3; // 1003
public static final int DISPOSED = CONTEXT_FIRST + 4; // 1004
public static final int DISPATCHED = CONTEXT_FIRST + 5; // 1005
public static final int REVERTED = CONTEXT_FIRST + 6; // 1006
public static final int ARRIVED = CONTEXT_FIRST + 7; // 1007
public static final int DEACTIVATED = CONTEXT_FIRST + 8; // 1008
public static final int ACTIVATED = CONTEXT_FIRST + 9; // 1009
public static final int STATE_CHANGED = CONTEXT_FIRST + 10; // 1010
public static final int SHOW_DOCUMENT = CONTEXT_FIRST + 12; // 1012
public static final int MESSAGE = CONTEXT_FIRST + 13; // 1013
public static final int NO_RESPONSE = CONTEXT_FIRST + 14; // 1014
public Object arg = null;
public AgletContext getAgletContext();
public AgletProxy getAgletProxy();
public String getMessage();
public String getText();
public URL getDocumentURL();
public ContextEvent(int id, Object context, AgletProxy target, Object arg);
public ContextEvent(int id, Object context, AgletProxy target);
}
The com.ibm.maf.MAFAgentSystem is an abstract class that defines
a set of method for creating, transferring, and managing agents. The interface
is (almost) equivalent to methods defined in the MASIF standard specification,
but not identical to the MASIF IDL, because this is not CORBA interface,
as mentioned before. See the Architecture
of Communication Layer section for more details of the architecture.
The getMAFAgentSystem primitive returns the instance of MAFAgentSystem
for the given address. The AgletsRuntime then uses this object to communicate
with the remote agent system. The returned object may be a stub object for
remote access, or a local server object if the address is its own.
package com.ibm.maf.MAFAgentSystem;
public class MAFAgentSystem {
static public MAFAgentSystem getMAFAgentSystem(Ticket ticket);
static public MAFAgentSystem getMAFAgentSystem(String address);
static public MAFAgentSystem getLocalMAFAgentSystem();
static public void initMAFAgentSystem(MAFAgentSystem runtime, Name name);
static public void startMAFAgentSystem(MAFAgentSystem runtime, String protocol);
// MASIF interfaces
public Name create_agent(Name name,
AgentProfile profile,
byte[] agent,
Object[] arguments,
ClassName[] class_names,
String code_base,
MAFAgentSystem class_provider);
public abstract void receive_agent(Name agent_name,
AgentProfile agent_profile,
byte[] agent,
String place_name,
ClassName[] class_names,
String code_base,
MAFAgentSystem class_sender);
public abstract byte[][] fetch_class(ClassName[] class_name_list,
String code_base,
AgentProfile agent_profile);
public abstract String find_nearby_agent_system_of_profile(AgentProfile profile);
public abstract MAFFinder get_MAFFinder();
public abstract AgentStatus get_agent_status(Name agent_name);
public abstract AgentSystemInfo get_agent_system_info();
public abstract AuthInfo get_authinfo(Name agent_name);
public abstract Name[] list_all_agents();
public abstract Name[] list_all_agents_of_authority(byte[] authority);
public abstract String[] list_all_places();
public abstract void resume_agent(Name agent_name);
public abstract void suspend_agent(Name agent_name);
public abstract void terminate_agent(Name agent_name);
}
The com.ibm.aglet.system.AgletRuntime abstract class provides an interface for creating and managing the context. The class actually used and the instance are automatically chosen and created by the framework. Applications should not try to create a runtime object by themselves. Instead, an application must use the getAgletRuntime() primitive to obtain a reference to the runtime object.
ThecreateAgletContext(String name) primitive creates a new instance of the runtime with the given name. If there is an existing context with the same name, the primitive throws an IllegalArgumentException. These contexts created in the AgletRuntime object can be obtained by either the getAgletContext(String name) or the getAgletContexts() primitive. removeAgletContext(AgletContext cxt) removes the specified context from the internal context table. A living AgletContext can be removed from the runtime table without shutdown. Such a context becomes invisible, and cannot be accessed either remotely or locally. This can be used to create a sort of "private" context into which no aglet be dispatched.
class AgletRuntime {
AgletRuntime getAgletRuntime();
public AgletContext createAgletContext(String name);
public AgletContext getAgletContext(String name);
public void removeAgletContext(AgletContext cxt);
public AgletContext[] getAgletContexts();
}
class Aglets {
public static AgletProxy createAglet(String contextAddress,
URL codebase,
String classname,
Object init);
public static AgletProxy getAgletProxy(String contextAddress, AgletID id);
public static AgletProxy[] getAgletProxies(String contextAddress);
}
You are not required to do the bootstrapping for client applications.
public void main(String args[]) throws Exception {
String contextAddress = "atp://host.name:434/contextName";
AgletProxy proxy = Aglets.createAglet(contextAddress, null,
"your.Aglet", null);
proxy.sendMessage(new Message("buy", "jewel"));
AgletProxy proxies[] = Aglets.getAgletProxies(contextAddress);
// dispose all aglets
for (int i=0; i < proxies.length; i++) {
proxies[i].dispose();
}
}
public class MyServer {
static private Opt options[] = {
Opt.Entry("-protocol", "maf.protocol", null),
};
static public void main(String args[]) {
Opt.setopt(options);
AgletRuntime runtime = AgletRuntime.init(args);
runtime.authenticate(username, password);
Name system_name = new Name(username.getBytes(), null, (short)1);
MAFAgentSystem maf_system = new MAFAgentSystem_AgletsImpl(runtime);
MAFAgentSystem.initMAFAgentSystem(maf_system, "atp");
Tahiti.installFactories();
//
// Creates a named context. To dispatch to this context, you have to
// specify a destination such as "atp://aglets.trl.ibm.com:434/test"
//
AgletContext cxt = runtime.createAgletContext("test");
ContextListener listener = new ContextAdapter () {
public void agletArrived(ContextEvent ev) {
AgletProxy proxy = ev.getAgletProxy();
try {
System.out.println("Aglet is arriving."+
proxy.getAgletInfo());
} catch (InvalidAgletException ex) {
ex.printStackTrace();
}
}
public void agletDispatched(ContextEvent ev) {
AgletProxy proxy = ev.getAgletProxy();
try {
System.out.println("Aglet is leaving."+
proxy.getAgletInfo());
} catch (InvalidAgletException ex) {
ex.printStackTrace();
}
}
}
cxt.addContextListener(listener);
Tahiti.installSecurity();
//
// Start a context
//
cxt.start();
MAFAgentSystem.startMAFAgentSystem(maf_system, protocol);
//
AgletProxy myAglet = cxt.createAglet(null, "MyAglet", null);
myAglet.sendMessage(new Message("startTrip"));
}
}
| Property Name | Description | Example |
|---|---|---|
| aglets.class.path | Specifies the default lookup path for a codebase. This is used when a null value is given as a codebase in the AgletContext.createAglet(..) primitive. | /usr/local/AWB/Aglets/public:/myhome/public C:\AWB\Aglets\public:D:\myaglets\public |
| aglets.viewer | Specifies the class name that is used for the server.
The class must implement a ContextListener interface. |
com.ibm.aglets.tahiti.Tahiti |
| Property Name | Description | Example |
|---|---|---|
| atp.useHttpProxy | Switch on and off the http proxy. | true/false |
| atp.http.proxyHost | Host | firewall.ibm.com |
| atp.http.proxyPort | Port number | 8080 |
| atp.noProxy | The proxy is not used for the hosts whose addresses start with the string specified here. | ibm.com |
| Property Name | Description | Example |
|---|---|---|
| tahiti.browser_command | openurl | Specifies the command to launch the browser |