Jade Programming Tutorial For Beginners
Jade Programming Tutorial For Beginners
JADE - Java Agent DEvelopment Framework is a framework to develop multi-agent systems in compliance with the FIPA
specifications. JADE successfully passed the 1st FIPA interoperability test in Seoul (Jan. 99) and the 2nd FIPA interoperability test
in London (Apr. 01).
Copyright (C) 2000 CSELT S.p.A. (C) 2001 TILab S.p.A. (C) 2002 TILab S.p.A.
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, version 2.1 of the License.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
TABLE OF CONTENTS
1 JADE OVERVIEW 4
5.6 Selecting messages with given characteristics from the message queue 18
5.7 Complex conversations 19
This tutorial is structured as follows. Chapter 1 gives a brief overview of JADE and introduces the
concepts of Platform, Container, AMS and DF. Chapter 2 presents a simple example that will be used
throughout this tutorial to illustrate the steps required to develop simple agent-based applications with
JADE. Chapter 3 focuses on creating agents and explains the basic features of the Agent and AID classes.
Chapter 4 explains how to make JADE agents execute tasks and introduces the Behaviour class. Chapter 5
describes how to make agents communicate and introduces the ACLMessage and MessageTemplate
classes. Chapter 6 illustrates how to exploit the Yellow Pages service provided by the DF agent through the
DFService class.
Besides the basic features illustrated in this tutorial JADE provides a number of advanced features such
as the support for complex interaction protocols and the usage of user defined ontologies. For these features
readers are reminded to the JADE Programmer’s guide, Administrator’s guide and specific tutorials
available on the JADE web site.
1 J A D E O V E RV I E W
If another main container is started somewhere in the network it constitutes a different platform to
which new normal containers can possibly register. Figure 1 illustrates the above concepts through a sample
scenario showing two JADE platforms composed of 3 and 1 containers respectively. JADE agents are
identified by a unique name and, provided they know each other’s name, they can communicate
transparently regardless of their actual location: same container (e.g. agents A2 and A3 in Figure 1),
different containers in the same platform (e.g. A1 and A2) or different platforms (e.g. A4 and A5).
Developers don’t have to know how the JADE runtime environment works. They just need to start it
before executing their agents. Starting JADE as a main or normal container and executing agents on it,
is described in the JADE Administrative Tutorial or in more details in the Administrator’s Guide
available on the JADE website. For convenience, however, a few examples are reported below.
The following command line launches a Main Container activating the JADE management GUI (-gui)
option. <classpath> must include all jade classes plus all required application-specific classes.
The following command line launches a peripheral container (-container option) that registers to a main
container running on host avalon.tilab.com (-host option) and activates an agent called john of class
myPackage.MyClass (-agents) option
The AMS (Agent Management System) that provides the naming service (i.e. ensures that each agent in
the platform has a unique name) and represents the authority in the platform (for instance it is possible to
create/kill agents on remote containers by requesting that to the AMS). This tutorial does not illustrate how
to interact with the AMS as this is part of the advanced JADE programming.
The DF (Directory Facilitator) that provides a Yellow Pages service by means of which an agent can
find other agents providing the services he requires in order to achieve his goals. Using the Yellow Pages
service provided by the DF agent is illustrated in chapter 6.
A1
AMS DF
Main container
Is registered
A4 Is registered with A2 A3
with
Container 2
Platform 1 Container 1
Network
A5
AMS DF
Main container
Platform 2
This chapter introduces a simple example that will be used throughout this tutorial to illustrate the steps
required to develop agent-based applications with JADE. The scenario considered in this example includes
some agents selling books and other agents buying books on behalf of their users.
Each buyer agent receives the title of the book to buy (the “target book”) as a command line argument
and periodically requests all known seller agents to provide an offer. As soon as an offer is received the
buyer agent accepts it and issues a purchase order. If more than one seller agent provides an offer the buyer
agent accepts the best one (lowest price). Having bought the target book the buyer agent terminates.
Each seller agent has a minimal GUI by means of which the user can insert new titles (and the
associated price) in the local catalogue of books for sale. Seller agents continuously wait for requests from
buyer agents. When asked to provide an offer for a book they check if the requested book is in their
catalogue and in this case reply with the price. Otherwise they refuse. When they receive a purchase order
they serve it and remove the requested book from their catalogue.
All issues related to electronic payment are outside the scope of this tutorial and are not taken into
account.
The complete sources of this example are available among the examples provided with JADE in the
examples.bookTrading package.
3 C R E AT I N G J A D E A G E N T S – T H E A G E N T C L A S S
Creating a JADE agent is as simple as defining a class extending the jade.core.Agent class and
implementing the setup() method as shown in the code below.
import jade.core.Agent;
The first part of the above output is the JADE disclaimer that is printed out each time the JADE runtime
is started. The initialization of the kernel services (kernel services are JADE internal stuff and are not
described in this document) activated in the started platform follows. Finally the indication that a container
called “Main-Container” is ready completes the JADE runtime startup. When the JADE runtime is up our
agent is started and prints its welcome message. The nickname of the agent is “buyer” as we specified on
the command line. The platform name “NBNT2004130496:1099/JADE” is automatically assigned on the
basis of the host and port you are running JADE on (see the JADE Administrator’s guide for assigning a
name to the platform).
import jade.core.Agent;
import jade.core.AID;
1
Note that the list of known seller agents to send requests to is fixed. In chapter 6 we will describe how to dynamically discover
them.
// Put agent initializations here
protected void setup() {
// Printout a welcome message
System.out.println(“Hello! Buyer-agent “+getAID().getName()+” is ready.”);
4 A G E N T TA S K S – T H E B E H AV I O U R C L A S S
As mentioned in chapter 3, the actual job an agent has to do is typically carried out within “behaviours”.
A behaviour represents a task that an agent can carry out and is implemented as an object of a class that
extends jade.core.behaviours.Behaviour. In order to make an agent execute the task implemented
by a behaviour object it is sufficient to add the behaviour to the agent by means of the addBehaviour()
method of the Agent class. Behaviours can be added at any time: when an agent starts (in the setup()
method) or from within other behaviours.
Each class extending Behaviour must implement the action() method, that actually defines the
operations to be performed when the behaviour is in execution and the done() method (returns a boolean
value), that specifies whether or not a behaviour has completed and have to be removed from the pool of
behaviours an agent is carrying out.
2
When using JADE with the LEAP add-on, arguments are separated by colon (‘;’) instead of spaces.
behaviour is scheduled for execution its action() method is called and runs until it returns.
Therefore it is the programmer who defines when an agent switches from the execution of a behaviour to
the execution of the next one.
Though requiring a small additional effort to programmers, this approach has several advantages.
• Allows having a single Java thread per agent (that is quite important especially in environments with
limited resources such as cell phones).
• Provides better performances since behaviour switch is extremely faster than Java thread switch.
• Eliminates all synchronization issues between concurrent behaviours accessing the same resources (this
speed-up performances too) since all behaviours are executed by the same Java thread.
• When a behaviour switch occurs the status of an agent does not include any stack information and is
therefore possible to take a “snapshot” of it. This makes it possible to implement important advanced
features e.g. saving the status of an agent on a persistent storage for later resumption (agent
persistency) or transferring it to another container for remote execution (agent mobility). Persistency
and mobility are advanced JADE features and are outside the scope of this tutorial however.
3
In JADE there is a single Java thread per agent. Since JADE agents are written in Java, however, programmers may start new Java
threads at any time if they need. If you do that, remember to pay attention since the advantages mentioned in this section are no
longer valid.
setup()
- Initializations
- Addition of initial behaviours
b.action()
- Agent “life” (execution of
behaviours)
NO
b.done()?
YES
Taking into account the described scheduling mechanism it is important to stress that a behaviour like
that reported below prevents any other behaviour to be executed since its action() method never returns.
1) “One-shot” behaviours that complete immediately and whose action() method is executed only once.
The jade.core.behaviours.OneShotBehaviour already implements the done() method by
returning true and can be conveniently extended to implement one-shot behaviours.
public class MyOneShotBehaviour extends OneShotBehaviour {
public void action() {
// perform operation X
}
}
Operation X is performed only once.
2) “Cyclic” behaviours that never complete and whose action() method executes the same operations
each time it is called. The jade.core.behaviours.CyclicBehaviour already implements the done()
method by returning false and can be conveniently extended to implement cyclic behaviours.
public class MyCyclicBehaviour extends CyclicBehaviour {
public void action() {
// perform operation Y
}
}
Operation Y is performed repetitively forever (until the agent carrying out the above behaviour terminates).
3) Generic behaviours that embeds a status and execute different operations depending on that status. They
complete when a given condition is met.
public class MyThreeStepBehaviour extends Behaviour {
private int step = 0;
public void action() {
switch (step) {
case 0:
// perform operation X
step++;
break;
case 1:
// perform operation Y
step++;
break;
case 2:
// perform operation Z
step++;
break;
}
}
1) The WakerBehaviour whose action() and done() methods are already implemented in such a way
to execute the handleElapsedTimeout() abstract method after a given timeout (specified in the
constructor) expires. After the execution of the handleElapsedTimeout() method the behaviour
completes.
public class MyAgent extends Agent {
protected void setup() {
System.out.println(“Adding waker behaviour”);
addBehaviour(new WakerBehaviour(this, 10000) {
protected void handleElapsedTimeout() {
// perform operation X
}
} );
}
}
Operation X is performed 10 seconds after the “Adding waker behaviour” printout appears.
2) The TickerBehaviour whose action() and done() methods are already implemented in such a way
to execute the onTick() abstract method repetitively waiting a given period (specified in the constructor)
after each execution. A TickerBehaviour never completes.
public class MyAgent extends Agent {
protected void setup() {
addBehaviour(new TickerBehaviour(this, 10000) {
protected void onTick() {
// perform operation Y
}
} );
}
}
Operation Y is performed periodically every 10 seconds.
4.4 Behaviours required in the book trading example
Having described the basic types of behaviour, let’s move now to analyse which behaviours have to be
carried out by the Book-buyer agent and Book-seller agent of our book trading example.
The RequestPerformer behaviour actually dealing with the request to seller agents will be described
in chapter 5 where we will discuss agent communication.
import jade.core.Agent;
import jade.core.behaviours.*;
import java.util.*;
// Add the behaviour serving requests for offer from buyer agents
addBehaviour(new OfferRequestsServer());
/**
This is invoked by the GUI when the user adds a new book for sale
*/
public void updateCatalogue(final String title, final int price) {
addBehaviour(new OneShotBehaviour() {
public void action() {
catalogue.put(title, new Integer(price));
}
} );
}
}
The BookSellerGui class is a simple Swing GUI and is not presented here since it is outside the scope of
this tutorial. Its code is available among the sources packaged with this tutorial.
5 A G E N T C O M M U N I C AT I O N – T H E A C L M E S S A G E C L A S S
One of the most important features that JADE agents provide is the ability to communicate. The
communication paradigm adopted is the asynchronous message passing. Each agent has a sort of mailbox
(the agent message queue) where the JADE runtime posts messages sent by other agents. Whenever a
message is posted in the message queue the receiving agent is notified. If and when the agent actually picks
up the message from the message queue to process it is completely up to the programmer however.
• The communicative intention (also called “performative”) indicating what the sender intends to
achieve by sending the message. The performative can be REQUEST, if the sender wants the receiver
to perform an action, INFORM, if the sender wants the receiver to be aware a fact, QUERY_IF, if the
sender wants to know whether or not a given condition holds, CFP (call for proposal), PROPOSE,
ACCEPT_PROPOSAL, REJECT_PROPOSAL, if the sender and receiver are engaged in a negotiation,
and more.
• The content i.e. the actual information included in the message (i.e. the action to be performed in a
REQUEST message, the fact that the sender wants to disclose in an INFORM message …).
• The content language i.e. the syntax used to express the content (both the sender and the receiver must
be able to encode/parse expressions compliant to this syntax for the communication to be effective).
• The ontology i.e. the vocabulary of the symbols used in the content and their meaning (both the sender
and the receiver must ascribe the same meaning to symbols for the communication to be effective).
• Some fields used to control several concurrent conversations and to specify timeouts for receiving a
reply such as conversation-id, reply-with, in-reply-to, reply-by.
/**
Inner class OfferRequestsServer.
This is the behaviour used by Book-seller agents to serve incoming requests
for offer from buyer agents.
If the requested book is in the local catalogue the seller agent replies
with a PROPOSE message specifying the price. Otherwise a REFUSE message is
sent back.
*/
private class OfferRequestsServer extends CyclicBehaviour {
public void action() {
ACLMessage msg = myAgent.receive();
if (msg != null) {
// Message received. Process it
String title = msg.getContent();
ACLMessage reply = msg.createReply();
The createReply() method of the ACLMessage class automatically creates a new ACLMessage
properly setting the receivers and all the fields used to control the conversation (conversation-id, reply-with,
in-reply-to) if any.
If we look at Figure 2, however we may notice that, when we add the above behaviour, the agent’s
thread starts a continuous loop that is extremely CPU consuming. In order to avoid that we would like to
execute the action() method of the OfferRequestsServer behaviour only when a new message is
received. In order to do that we can use the block() method of the Behaviour class. This method marks
the behaviour as “blocked” so that the agent does not schedule it for execution anymore. When a new
message is inserted in the agent’s message queue all blocked behaviours becomes available for execution
again so that they have a chance to process the received message. The action() method must therefore be
modified as follows.
5.6 Selecting messages with given characteristics from the message queue
Considering that both the OfferRequestsServer and PurchaseOrdersServer behaviours are
cyclic behaviour whose action() method starts with a call to myAgent.receive(), you may have
noticed a problem: how can we be sure that the OfferRequestsServer behaviour picks up from the
agent’s message queue only messages carrying requests for offer and the PurchaseOrdersServer
behaviour only messages carrying purchase orders? In order to solve this problem we must modify the code
we have presented so far by specifying proper “templates” when we call the receive() method. When a
template is specified the receive() method returns the first message (if any) matching it, while ignores all
non-matching messages. Such templates are implemented as instances of the
jade.lang.acl.MessageTemplate class that provides a number of factory methods to create templates
in a very simple and flexible way.
As mentioned in 5.3, we use the CFP performative for messages carrying requests for offer and the
ACCEPT_PROPOSAL performative for messages carrying proposal acceptances, i.e. purchase orders.
Therefore we modify the action() method of the OfferRequestsServer so that the call to
myAgent.receive() ignores all messages except those whose performative is CFP.
/**
Inner class RequestPerformer.
This is the behaviour used by Book-buyer agents to request seller
agents the target book.
*/
private class RequestPerformer extends Behaviour {
private AID bestSeller; // The agent who provides the best offer
private int bestPrice; // The best offered price
private int repliesCnt = 0; // The counter of replies from seller agents
private MessageTemplate mt; // The template to receive replies
private int step = 0;
It is important to stress that the blockingReceive() methods actually blocks the agent thread.
Therefore if you call blockingReceive() from within a behaviour, this prevents all other behaviours to
run until the call to blockingReceive() returns. Taking into account the above consideration a good
programming practice to receive messages is: use blockingReceive() in the setup() and takeDown()
methods; use receive() in combination with Behaviour.block() (as shown in 5.5) within behaviours.
6 T H E Y E L L O W PA G E S S E RV I C E – T H E D F S E RV I C E C L A S S
In the code we have written so far we have assumed that there is a fixed set of seller agents (seller1 and
seller2) and that each buyer agent already knows them (see the AID[] sellerAgents member variable of
the BookBuyerAgent class in the code presented in 3.4). In this chapter we describe how to get rid of this
assumption and exploit the yellow pages service provided by the JADE platform to make buyer agents
dynamically discover seller agents available at a given point in time.
A1: - serviceX
A1 - serviceY A4
A2: - serviceZ
Publish
Search for
provided A3: - serviceW required
services - serviceK services
- serviceH
A2
A5
Yellow Pages service
Exploit required
A3 service A6
The yellow pages service in JADE (according to the FIPA specification) is provided by an agent called
DF (Directory Facilitator). Each FIPA compliant platform hosts a default DF agent (whose local name is
“df”). Other DF agents can be activated and several DF agents (including the default one) can be federated
so that to provide a single distributed yellow pages catalogue.
In order to publish a service an agent must create a proper description (as an instance of the
DFAgentDescription class) and call the register() static method of the DFService class. Typically,
but not necessarily, service registration (publication) is done in the setup() method as shown below in the
case of the Book seller agent.
The search() static method of the DFService class can be used as exemplified in the code used by
the Book buyer agent to dynamically find all agents that provide a service of type “book-selling”.