Tutorial: Developing Multiagent Systems with Boris This document describes the use of the Boris Management Console and provides an introduction to the development of multiagent systems using Boris tools. Boris is a platform based on the general multiagent paradigm. It provides an API built on Java and a set of supporting tools and utilities. The main supporting tool is the Console which is the visual front end that aids deployment and monitoring. A number of examples to illustrate multiagent systems development are available for download. Set up Boris is available as a single Java Jar file to download. Once you have downloaded this Jar file, you can develop multiagent systems in Java using the API. The instructions below indicate the steps involved. 1. download and save boris.jar to your workspace 2. set the classpath to point to boris.jar [ example contents of a batch file used to set classpath set CLASSPATH=.; F:\dev\jars\boris.jar; ] You can now write Java programs using agents. Note that to use the Boris API you must add the relevant import statements at the top of your Java files: boris.kernel – contains the core functionality boris.monitor – the boris console boris.utils – contains useful utilities for development See the API Documentation for full details. Boris Concepts Agents in Boris have the basic capability expected of all types of agent. Firstly they can run as independently executable entities or be embedded in other independently executable entities. Secondly, agents can send and receive messages. Specialised classes of agent provide additional qualities. Communication between agents is facilitated by platform components called portals. Agents join a Boris MAS by associating themselves with a portal. Portals facilitate communication with those agents that share a virtual machine (VM). agent agent portal agent fig 1: single portal MAS with monitor in one VM 1 The following code samples show how programmers create agents in Java. This is similar in syntax to creating any other object in Java: Agent a = new Agent( "agentA" ); The following code creates a portal and associates an agent a to it: Portal p = new Portal(“portal”); p.addAgent(a); Agents can now send messages to each other. The following code shows agentA sending a message to agentB: a.sendMessage(“agentB”,"requesting information"); If the agent receives messages and performs some action, this is programmed as shown below: a . addMessageListener ( new MessageListener( ) { Public void messageReceived( String from, String to, String msg, MsgId msgId ) { // agent functional code } }); The messageReceived method executes when the agent receives a message. The agent will then typically decide on a course of action in relation to the message received. Communication links between different virtual machines and physical machines are provided by Routers. Portals connect to routers in order to provide their agents with communication across machines. The following diagrams show alternate configurations for a distributed multiagent system. agent agent agent portal router VM/Machine 1 portal agent VM/Machine 2 fig 2: distributed MAS 1 2 agent agent agent portal VM/ Machine 1 router VM/ Machine 2 portal agent VM/ Machine 3 fig 3: distributed MAS 2 agent router agent agent portal portal agent agent portal VM/Machine 1 VM/Machine 2 agent agent portal router agent portal agent portal fig 4: distributed MAS 3 3 Routers are created in the same manner: Router router = new Router( “routername” ); router.advertiseConnection ( 1234 ); Once a router has advertised on a port number as shown above, portals and other routers can make a connection using this port number. Portals and routers (nodes) in other VMs and physical machines connect using the port number as well as the machine IP address of the advertising router. Agents that are attached to connected nodes may send each other messages. This means that agents can be developed independantly of the MAS structure, which may change on different installations. The following code shows how portal p, shown created earlier connects to an advertising router: // to connect to a router advertising on port 1234 on the same physical machine p.connectToGrid( 1234 ); // to connect to a router advertising on port 1234 // on a different machine with IP adress 151.71.6.21 p.connectToGrid( “156.71.6.21”, 1234 ); // this method call halts the current thread until a connection has been made. // This is useful as agents can only send messages after this connection procedure // has been completed p.waitForConnection(); The Boris Management Console The management console automates the setting up of MAS structure described in the previous section. This means that programmers include in their code only statements related to the setup of agents and their connection to portals. In addition, the console provides debugging, deployment and monitoring of the running multiagent agent. This tool in effect provides a visual front end for the Boris platform. To start up the Console, double click Boris.jar. Alternatively, type in “java -jar Boris.jar” at the command prompt. As soon as the Console starts a dialog box requesting a router name is displayed. This will be the name of a router that will be automatically created. The figure below shows the Console running. 4 fig 5: Boris Management Console on start up Note: If there are errors in the start up or functioning of the Console, an error text file is generated in the same directory as boris.jar. This will also contain the results of any system output messages from your programs. setting up the router A router is created automatically on startup with the name provided. This appears on the title bars of the windows. The router control panel can be used to configure the router. Firstly, the router needs to be advertising before connections can be made. To advertise: type in a port number and click the advertise button. If the router was able to successfully advertise on this port, a message indicating this appears in the messages tab area. To connect to another advertising router on the same machine, type in the port number and click connect. To connect to an advertising router on another machine, enter a. IP address as well as a portnumber and press enter. Agents attached to portals on this router can now communicate with agents attached to portals on the other router. creating portals Once the router is advertising, portals can be created: select portal->create new from the menu enter a name for the portal and create ok. The new portal should now be visible in the portal activity monitor window This portal is now connected to the router on the advertised port 5 writing agents Once the portals have been created, agents can be started up and attached to a specified portal. This requires that constructors of classes containing agents adhere to specific format, they may take as argument one value of type Portal and and optional argument of type String to be used as the agent name. The code below shows a simple example of a class containing an agent called InputAgent. The class is a JFrame that displays a text field and a button. The user enters a message into the text field and clicks on the button. When the button is clicked the agent sends the user text to an agent called OutputAgent. import boris.kernel.*; public class BasicInput extends JFrame { … public BasicInput(Portal iPortal) { //--- set up agent ---final Agent iAgent = new Agent( "InputAgent" ); iPortal.addAgent(iAgent); //--- setup GUI and listeners --final JTextField msg = new JTextField(20); JButton submit = new JButton( "Send" ); submit.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { iAgent.sendMessage("OutputAgent", msg.getText() ); } }); //--- layout GUI --setLayout( new BorderLayout() ); add( new Label(" Click to send message"), BorderLayout.NORTH ); add(msg, BorderLayout.CENTER); add( submit, BorderLayout.SOUTH ); //--- final ---pack(); setVisible(true); } // alternate constructor public BasicInput(Portal iPortal, String agentName) { //--- set up agent ---final Agent iAgent = new Agent( agentName ); … } } 6 Note: the import statement paramenter of type portal. The agent is added to this portal the agent is declared final. This is reccomended for safety and is required in cases where the agent is referred to in inner classes. In this case it is referred to in the ActionListener class of the Submit Button the call to pack and setVisible(). These should be the last lines in the constructor. starting agents from the console select Agents-> startup from the menu. This brings up the following dialog box fig 6: start agent dialog box from the portal dropdown box choose an existing portal or, select create new and type in a name for the portal in the textfield. click the select button to browse and find the agent file. in the Cmd Line textfield, type in an extra argument to pass to the class containing the agent, or leave this blank depending on the constructor implemented. click ok to start the agent. If the agent does not startup, see the console window for any error messages. possible causes are: o an agent of that name is already registered o the class constructor does not have type Portal as its first parameter, and if it has a second parameter, it is not of type String. The constructor may not have a third parameter. o agent is not a window type object e.g. JFrame 7 monitoring Portals and routers, on instantiation are associated with a portalMonitor and routerMonitor respectively. These monitors receive information from the attached router or portal about agent activity. They are provided as interfaces that may be implemented by developers who wish to build their own tools. This has the ability to provide information regarding portal-level and router-level activity. Management Console Monitor Monitor portal agent router . . . . router portal agent fig 7: Mas with monitors This diagram shows how the console is able to track information related to a router and all its attached portals. Note that information concerning agent activity in other routers (even though they may be connected) is not available. Monitoring is at the level of Router. fig 8: console monitoring a MAS 8 The Portal Activity Monitor window displayed beneath the Router Control Panel shows a list of portals associated with the current router. Messages that are passed between agents are displayed on the right hand side. The checkboxes that appear next to the agent names can be used to filter messages based on sending and receiving agents. debugging In order to support debugging of MAS structure and connections, and as a test harness for testing and debugging of single agents independantly from their MAS, a utility agent called the DebugAgent is provided as part of the Boris toolkit. This can be run from the Console: Select Agents -> run Debug Agent from the menu. This bring up the following dialog box. fig 9: start debug agent dialog box From the drop down list, select the portal that the DebugAgent should be attached to, or select create new and enter a name for the portal. Enter a name for the debug agent and click ok. This starts up a debug agent in the chosen portal. It should appear in the portal activity monitor. fig 10: debug agent This agent can send a message to any other agent in the MAS. Type in the name of the agent to send the message to, and the message and click submit. The message should appear in the table above. 9 Additionally, any agent in the MAS can send this agent a message by using its name (in this case Agent01, this appears in the title bar). All messages received will also be displayed in the table above. These messages should also appear in the monitor window on the console. Using Boris Utilities Exit Dialog This is a convenience class for attaching an exit listener to window objects like JFrames. It listens for a user click the window’s close button . A swing dialog box is displayed. Choosing ok will close the JFrame and exit the application. to use 1. import boris.utils.ExitDialog; 2. include these lines in window setup: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE ); addWindowListener( new ExitDialog() ); or in the main class that creates windows: MyFrame A1 = new MyFrame(); A1.addWindowListener(new ExitDialog()); A1.setVisible(true); A1.setLocation(50,20); The above code results in the following dialogbox being displayed when the application exits: fig 11: ExitDialog 3. additional arguments may be specified. A string to replace the text “exit application” may be provided : addWindowListener(new ExitDialog("The system will now close.") ExitNoDialog This is similar to the ExitDialog class except that when the window’s close button is clicked, the JFrame is closed and System.exit is called without displaying a dialogbox for confirmation. Usage is similarti ExitDialog: addWindowListener( new ExitNoDialog() ); 10 Ok Cancel Dialog Import boris.utils.Dlg This is a utility class. It provides a JDialog with a panel that contains ok and cancel buttons. This is an abstract class that should be extended. Classes that extend Dlg should; add panel pnlOkCancel to their GUI., call initialise(), implement the abstract method okClick(); When the user clicks the OK button on pnlOkCancel, okClick() runs and the dialogbox is disposed. Clicking cancel causes the dialog box to be disposed. Example use: 1. Define a class that extends Dlg: public class MyDialog extends Dlg { public DlgMasStart() { super( parentFrame ); // parentFrame is the JFrame on which the dialogbox is to be centered . // set up your GUI add( pnlOkCancel ); // pnlOkCancel defined in superclass. // it contains ok and cancel buttons . . initialise(); } // superclass method. runs when ok button clicked public boolean okClick() { . // include your code here return true; } } 2. The class can be called from a JFrame like this: new MyDialog( this ); 3. The code shown above as it is creates a dialog box that looks like this: fig 12: blank Dlg Any GUI that you aet up appears above the OK/Cancel buttons. 11 Clicking Cancel simply disposes the dialogbox. Clicking ok causes the code in public boolean okClick() to run before the dialogbox is disposed. FileUtils import boris.utils.FileUtils; This class contains a number of useful functions for file handling. Using them shortens and neatens your code. There are some static methods and non-static methods. To use the static methods, you do not need to create an object from file utils: There are two methods that take a string representing a full file name and return the name without the extension and the extension. See below: File file = .........; String FullFileName = file.getName( ); String name = String ext = FileUtils.removeExtension( FullFileName ); FileUtils.getExtension( file ) To use non-static methods you must instantiate the class: fileutils = new FileUtils ( ); To call up an open file dialog box that returns the file the user selected as a java file object: File file = fileutils.openFile(window); window refers to the name of a window object like JFrame that the dialog box should be associated with. To save text into files: Vector<String> data = new Vector<String>(); fileutils.saveFile(window, data); 12