previous   next   contents   commands   index  

8. Tutorial

OK so you've done a bit of (perhaps boring) reading, lets build something. If you like to learn by example this is a good point to begin. In this section, we'll script a couple of small, working example step by step. First, we do some setup.

I assume you've built and installed AGNI. Set up the environment variable AGNI_HOME to the directory where you installed the distribution (/usr/local/agni by default). We will run 2 Agent Daemon on two computers - say crowbar.ncsl.nist.gov and badger.antd.nist.gov and let crowbar.ncsl.nist.gov be the Session Leader. I assume you have the port 1234 unassigned. Set the environment variables AGNI_SESSION and AGNI_LOCAL to crowbar.ncsl.nist.gov:1234. From an x-terminal window on crowbar start the Session Leader by typing:

agnid -h $AGNI_SESSION
      

Similarly from badger start a second Agent Daemon:

agnid -h $AGNI_SESSION
      

Start an external shell on crowbar to talk to the Daemons (I assume you are running tcl/tk 8.0 as a production system):

tclsh 
% load $env(AGNI_HOME)/lib/libetcl.so Etcl
% join_session $env(AGNI_LOCAL)
      

List the Daemons who are currently in the Session. From your shell of the last step, type the following :

% get_active_peer_map
      

And verify that you see a list of pairs consisting of an integer showing the Daemon identifier and an IP address for the Daemon.

Our first example consists of a simple post-it note. We would like to send a message from one machine (crowbar) to another (badger) and print a reminder in the terminal window of the target machine (badger). Our application structure will consist of a MStream with an Agent attached to it. The Agent will register an on_stream_relocation Handler to post the message to the target Location. We script this example step by step:

First we create a MStream that will be our "messenger" and carry our post-it note over to the other machine. From the tclsh shell on crowbar (that you opened in the last step):

% set tstream [stream_create messenger -temp]
      

List the MStreams that the system supports. From the tclsh prompt do:

% list_streams
      

You will see a list of MStreams along with the Locations. Verify that the MStream list contains a temporary MStream with the name prefix "messenger". We use the -temp flag here because we don't really care what the MStream name is - all we want is a unique one.

Now register an Agent with the messenger that will deliver the message. From the same tclsh prompt, type:

% register_agent $tstream {} {
      add_briefcase message
      
      on_stream_append {
            set target [lindex $argv 0]
            set message [lindex $argv 1]
            stream_relocate $target
	  }
      on_stream_relocation {
            puts $message
      }
}
      

In the Agent above, the messenger puts a variable called message in its briefcase (add_briefcase message). It places any message sent to it in this variable (set message [lindex $argv 1] in the on_stream_append handler) and prints it on the screen on arrival at the target Location (puts $message in the on_stream_relocation handler).

You can obtain the identifier for the Agent you just created (again from the command prompt type):

% list_agents $tstream
      

Now we need to communicate with the Agent we just created by sending the MStream messages. Lets say we would like to send a post-it note to Location 1 (badger). We "append" a pair consisting of a Location and the message we would like to send. From the shell command prompt (on crowbar) type:

% stream_append $tstream [list 1 "Hello world"]
      

Verify that this results in the message "Hello world" appearing on the console at Location 1. Now we can clean up the MStreams using a stream_destroy command from the shell:

% stream_destroy $tstream
      

Clearly, we do not really need mobile Agents to achieve this, so lets script our example using more conventional means. In this case we will use two MStreams. One at the sender Location (crowbar) and another at the receiver Location (badger). We will send a message to the receiver from the sender. The receiver will print the message on the console upon receipt.

First begin by creating the sender and receiver MStreams (from the tclsh shell) on crowbar:

% set receiver [stream_create receiver -temp]
% set sender [stream_create sender -temp]
      

Register Agents with the sender and receiver. First register an Agent with the sender MStream:

% register_agent $sender $receiver {
	add_briefcase receiver
        set receiver $argv
	stream_open $receiver

	on_stream_append {
		set message $argv
		stream_append $receiver $message
	}
}
      

Next register an Agent with the receiver MStream:

% register_agent $receiver {} {
	on_stream_append {
		puts $argv
	}
}
      

Next, move the receiver and sender MStreams to the appropriate Locations (receiver on badger, sender on crowbar):

% stream_move $receiver 1
% stream_move $sender 0
      

And verify that the MStreams are at the right Locations using the list_streams command from the shell.

Now, communicate with the receiver via the sender by appending messages to the sender from the shell on crowbar:

% stream_append $sender "Hello world"
      

And observe that the string "Hello world" appears at the agnid console at Location 1 (badger).

Note that in this version of our example, we did not use movement during operation - just during setup. The application structure was simplified and made more intuitive by using relocation during operation. However, relocation involves moving MStream state around - an operation that may not always be cheap.

So far, we have not included any system integrity features. Thus anybody who opens the MStream can modify the system by adding new Agents, deleting MStreams and so on. Now we add system integrity features to our example to prevent this from occurring.

For an application structure, we choose the first version because of its simplicity. We specify a Stream Controller when we create the MStream that prevents new Agents (after the first one) from being attached and any registered Agent from being killed.

% set tstream [ stream_create  messenger -temp -controller {
	on_stream_new_agent {
		# Get the currently attached Agents
		set agent_list [list_agents]
		# Get the Location where the MStream was created
		set creator [stream_creator]
		# Allow only two Agents from the creator (the Stream
                # Controller is considered as an Agent).
		if { [llength $agent_list] > 1 || \
                                  [get_sender_location] != $creator } {    
			return 1
		}
                return 0
	}

	on_stream_agent_kill {
		# Tolerate no externally issued destructions
		# of Agents.
		return 1
	}
				
}]
      

The remainder of the application is the same as in the previous case so we do not repeat it. Next, we specify a System Controller script that only allows Location 0 to create MStreams and only badger to join the Session . Place the following in a text file called authscript.tcl:

	
on_stream_creation {
	set creator [lindex $argv 0]
	if { $creator != 0 } {
		puts "Sorry cannot create!"
		return 1
	} else {
		return 0
	}
}
	
on_new_peer {
	set ipaddress [lindex $argv 1]

	if { $ipaddress != "129.6.55.16" } {
               # 129.6.55.16 is badger IP Address
               # Note that the Session Leader is always allowed 
               # to join its Session
               puts "Sorry access denied!"
	       return 1
	} else {
	       return 0
	}
}
		
      

Now kill the Agent Daemons from crowbar:

% leave_session
      

Kill the tclsh that you have previously started (Ctrl C) and re-start them.

From crowbar:

% agnid -h $AGNI_SESSION -s authscript.tcl
      

From badger:

% agnid -h $AGNI_SESSION
      

Restart the tcl shells on crowbar and on badger. As before, join the Session and attempt to create a MStream from badger and verify that the request was denied.

Now log into a third machine - say aries.antd.nist.gov and attempt to start an Agent Daemon.

From aries:

% agnid -h  $AGNI_SESSION
      

And notice that the request is denied.

Now lets say we want to be notified if our messenger faces a crash (the Agent Daemon when the MStream is living dies). We specify an on_stream_failure Handlers to detect this condition. The Agent code for the messenger changes and now looks as follows (on crowbar):

% set tstream [stream_create messenger -temp]
% register_agent $tstream {} {
      add_briefcase message
      
      on_stream_append {
            set target [lindex $argv 0]
            set message [lindex $argv 1]
            stream_relocate $target
      }
      on_stream_relocation {
            puts $message
      }	
      on_stream_failure {
	    puts "Warning!"
      }
}
% stream_append $tstream [list 1 "Hello world"]
      

Now if you kill the Agent Daemon running at Location1 (badger), you see the "Warning" message appears in the console at Location 0 (crowbar).

The example directory in this distribution has a more involved examples of this simple asynchronous chat using a Tk front end.


Feedback
Last modified: Wed Feb 10 15:15:42 EST 1999