Building Multi-Agent Systems with JADE


    Introduction
  1. Installing the software
  2. Your first programs
  3. Parallelism and Behaviours
  4. Agent communication
  5. Using the Directory Facilities (DF)
  6. Using Jade Behaviours
  7. Using ontologies
  8. --> Graphical Interfaces
  9. Mobility

8. Building agent with integrated GUI

In the Java programming language, a GUI runs on its own thread (the event-dispatching thread) that allows it to handle and react promptly to events that are generated whenever the user interacts with the GUI via a component such as pressing a button or resizing the window.. On the other hand, an agent program runs on its own execution thread which allows it to handle its behaviours. Because it is not efficient to let one thread call directly the methods of the other thread, JADE has provided an appropriate mechanism to manage interactions between the two threads when integrating a GUI with an agent.

The mechanism is simply based on event passing. Let's see how this mechanism works by considering the two directions of the interaction beween a GUI and an agent.

  1. The agent interacting with the GUI - A GUI has already a built-in mechanism of handling event which is implemented via the actionPerformed(...) method of every component that is registered with an ActionListener object. To register a component of your GUI with an ActionListener object, you either make your GUI implements the ActionListener interface and then register all interactive components of your GUI such as buttons with this ActionListener via the method addActionListener(...) or for each of the interactive components, you anonymously create an ActionListener object and add it to the component by passing it in argument to the same method addActionListener(). Whenever a call to the GUI is made, an ActionEvent is generated by the source component, that invokes the actionPerformed() method. And according to the code you provided within the actionPerformed() method, the GUI responds by processing the event. When your agent program interacts with the GUI, it just calls the method you provided within the GUI program that activates this mechanism.
  2. The GUI interacting with the agent - JADE has provided the abstract class GuiAgent that extends the Agent class. This class has two specific methods: postGuiEvent() and onGuiEvent(). These are the two methods that allow to handle the interactions between a GUI and an agent program. To be able to use these methods, your agent program must extend the GuiAgent class. Then you must provide the necessary code within the onGuiEvent() method that your agent will use to receive and process events that are posted by the GUI via the method postGuiEvent(). You may view the onGuiEvent() method as the equivalent of the actionPerformed() method in the GUI. When an agent program extending the GuiAgent class starts, it launches a specific behaviour - the GuiHandlerBehaviour - that handles incoming events from the GUI and dispatches them to the appropriate handlers, following exactly the same mechanism as in the GUI. To post an event to the agent, the GUI simply creates a GuiEvent object, adds the required parameters and passes it in argument to the method postGuiEvent(). Since this method belongs to the GuiAgent class, you need to provide to your GUI a reference to the agent class on which the GUI can invoke that method.

Let's view now how this works trough the version 3 of our bank example. Under the directory Bank-3-Gui, you have the two files BankClientAgent that contains the agent program and the BankAgentGui that contains the program of the integrated GUI. Note that the BankServerAgent has no GUI integrated. Here are the key points you must know when writing your GUI program. We have marked in bold in the following fragment of code took from the BankAgentGui class, these important aspects:

class BankAgentGui extends JFrame implements ActionListener, BankVocabulary {

Object[] actions = {"","New Account","Deposit","Withdrawal", "Balance","Operations"};
...
private JButton ok, cancel, quit;
private BankClientAgent myAgent; // Reference to the agent class


public BankAgentGui(BankClientAgent a) {

myAgent = a; // provide the value of the reference of BankClientAgent class here

...
}

public void actionPerformed(ActionEvent ae) {

//Process the event according to it's source
if (ae.getSource() == quit) {
shutDown();
}
else if (ae.getSource() == ok) {

if (menu.getSelectedItem().equals(actions[0])) {
alert("Select an action in the menu");
}
else if (menu.getSelectedItem().equals(actions[1])) {
GuiEvent ge = new GuiEvent(this, NEW_ACCOUNT);
myAgent.postGuiEvent(ge);

}
else if (menu.getSelectedItem().equals(actions[2]) ||
menu.getSelectedItem().equals(actions[3])) {
...
try {
float amount = Float.parseFloat(input.getText());
int type = menu.getSelectedItem().equals(actions[2])?
DEPOSIT : WITHDRAWAL;
GuiEvent ge = new GuiEvent(this, type);
ge.addParameter(accounts.get(acList.getSelectedIndex()));
ge.addParameter(new Float(amount));
myAgent.postGuiEvent(ge);

}
catch (Exception ex) { alert("Invalid input. Operation aborted!"); }
}
else if (menu.getSelectedItem().equals(actions[4]) ||
menu.getSelectedItem().equals(actions[5]) &&
status != IN_LINE) {
...

if (status == WAIT_CONFIRM) {
status = IN_LINE;
cancel.setEnabled(false);
GuiEvent ge = new GuiEvent(this, type);
ge.addParameter(accounts.get(acList.getSelectedIndex()));
myAgent.postGuiEvent(ge);

}
}
}
else if (ae.getSource() == cancel && status != IN_LINE) {
status = IN_PROCESS;
cancel.setEnabled(false);
msg.setText("Operation canceled!");
}
}
}// class BankAgentGui

The BankAgentGui class implements the ActionListener interface. It has a private attribute myAgent that is a reference to the owner agent class. This reference is obtained via the constructor of the class which takes as parameter a BankClientAgent object. This way, in the BankClientAgent class within the setup() method, the agent just calls the constructor of the BankAgentGui class, passing itself in parameter to it as follows:

public class BankClientAgent extends GuiAgent implements BankVocabulary {

...

transient protected BankAgentGui myGui; // Reference to the gui

protected void setup() {

....

// Instanciate the gui
myGui = new BankAgentGui(this);
myGui.setVisible(true);
}

...

protected void onGuiEvent(GuiEvent ev) {

// Process the event according to it's type
command = ev.getType();
if (command == QUIT) {
...
}
if (command == NEW_ACCOUNT) {
createAccount();
}
else if (command == DEPOSIT || command == WITHDRAWAL) {
Account acc = (Account)ev.getParameter(0);
float amount = ((Float)ev.getParameter(1)).floatValue();

requestOperation(acc, amount);
}
...
}

}

When the agent receives an event posted by the agent, it tests the type of the event by calling the method getType() on the GuiEvent object and processes the event by calling the appropriate handler. This requires you to define the types of the events that the agent is supposed to receive from the GUI. The events must be integer constants. In the above example, we defined the events QUIT, NEW_ACOOUNT, DEPOSIT AND WITHDRAWAL that are all integer constants. They are defined within the BankVocabulary interface which both the BankAgentGui and BankClientAgent classes implement. Notice that instead of defining them in an interface, you may also define them directly within the agent class and access them from the GUI via the unique reference to the agent class. Once the agent has identified the appropriate event with the GuiEvent object it can then retreive all the parameters that it contains by calling it'sgetParameter() method, assuming that these parameters had been added to the GuiEvent object before it was posted by the GUI.

 


Top
Previous
Next