In his tutorial on the subject, David Grimshaw describes succintly the role of the DF:
JADE implements a Directory Facilitator (DF) agent as specified by FIPA. The DF is often compared to the "Yellow Pages" phone book. Agents wishing to advertize their services register with the DF. Visiting agents can then ask (search) the DF looking for agents which provide the services they desire.
On his Jade site, prof. Grimshaw also has a page with pointers to the relevant FIPA standard documents.
Here, in keeping with our "Jade for dummies" (and students) philosophy, we will pass on our experience using the DF with micro examples.
The DF is a centralized registry of entries which associate service descriptions to agent IDs. The same basic data structure, the DFAgentDescription (DFD), is used both for adding an entry or searching for services. The difference is that when registering, you provide a complete description AND an AID; whereas when searching, you provide a partial description with no AID, The search returns an array of complete entries (with AIDs) whose attributes match your description and you can extract the ID of suitable agents from those entries.
The Jade documentation isn't all that clear on what should go into a DF Agent description and, to get an overview, it is best to refer directly to the FIPA document (pointer courtesy of D. Grimshaw).
For agents to interact usefully in open systems, it is imperative that they use the same language conventions and the same vocabulary. The DF entries thus concentrate on listing the ontologies, protocols and languages which are supported by the agents. Additionally, entries have sets of services which are characterized by a name and name-value properties as well as the ontology/language/protocol conventions they support [seems a bit redundant...]. Below is the structure of a DF Agent description. It appears quite complex, but most fields are optional and in actual practice we only need to use 1 or 2 attributes.
DFAgentDescription Name: AID // Required for registration Protocols: set of Strings Ontologies: set of Strings Languages: set of Strings Services: set of { { Name: String // Required for each service specified Type: String // Required ... Owner: String Protocols: set of Strings Ontologies: set of Strings Languages: set of Strings Properties: set of { Name: String Value: String } }Note: most of the attributes are sets of because an agent could handle messages in a variety of formats; this is reflected in the naming conventions for methods which most often start with add instead of set, for example: addOntologies( String onto )... Note also the unwarranted use of the plural, surprising because each method call adds ONE ontology at a time and if we want to specify several, we will have to call addOntologies multiple times.
Previously, we mentioned the importance in open applications of selecting agents based on ontologies, protocols and languages. But, for in-house applications like class projects, agent abilities are usually specified through other attributes. For example, in a market application with buyers and sellers, these could be described by using only service Type set either to "buyer" or to "seller".
import jade.domain.DFService; import jade.domain.FIPAAgentManagement.*; import jade.domain.FIPAException; .... DFAgentDescription dfd = new DFAgentDescription(); dfd.setName( getAID() ); try { DFService.register( this, dfd ); } catch (FIPAException fe) { fe.printStackTrace(); }This doesn't specify anything about the agent; just that it is available. Our next example is a bit more realsitic: here the agent wants to register as a buyer. There are many ways to do this and the conventions used must be known to other participating agents. In our example, we add a service description with Type "buyer". We also need to specify a name for the service because all service descriptions require a name. Commonly, we use the agent's local name. Here's how to do it:
DFAgentDescription dfd = new DFAgentDescription(); dfd.setName( getAID() ); ServiceDescription sd = new ServiceDescription(); sd.setType( "buyer" ); sd.setName( getLocalName() ); dfd.addServices(sd); try { DFService.register(this, dfd ); } catch (FIPAException fe) { fe.printStackTrace(); }When registering various agents, the only thing that usually changes is the Service Description, so you are well advised to package the repeated code in your own register method. Here is the one used in DF2.java
void register( ServiceDescription sd) { DFAgentDescription dfd = new DFAgentDescription(); dfd.setName(getAID()); dfd.addServices(sd); try { DFService.register(this, dfd ); } catch (FIPAException fe) { fe.printStackTrace(); } }And here is how it is used:
ServiceDescription sd = new ServiceDescription(); sd.setType( "buyer" ); sd.setName( getLocalName() ); register( sd );
Attempts to register an agent already in the DF gives an Exception. |
The usual way to deregister is in a method with the name takeDown(). Jade calls takeDown automatically when the agent dies. Here is a typical example:
protected void takeDown() { try { DFService.deregister(this); } catch (Exception e) {} }Even with this precaution, it is a common problem when restarting a failed agent program to find that registration fails because the old DF entry is still around. In our final example, we give a more complete register() method that overcomes this problem.
DFAgentDescription dfd = new DFAgentDescription(); ServiceDescription sd = new ServiceDescription(); sd.setType( "buyer" ); dfd.addServices(sd); DFAgentDescription[] result = DFService.search(this, dfd); System.out.println(result.length + " results" ); if (result.length>0) System.out.println(" " + result[0].getName() );The program SearchDF1.java tries several searches. Interesting points:
Our final DF example is SearchDF2.java. It does the same tests as SearchDF1 but it contains 3 generally useful utility methods to access the DF. First is an expanded register method which tests and removes old duplicate DF entries before adding a new one. Second are two utility search methods based on a ServiceType String which return either ONE or ALL matching agents.
AID getService( String service ) { DFAgentDescription dfd = new DFAgentDescription(); ServiceDescription sd = new ServiceDescription(); sd.setType( service ); dfd.addServices(sd); try { DFAgentDescription[] result = DFService.search(this, dfd); if (result.length>0) return result[0].getName() ; } catch (FIPAException fe) { fe.printStackTrace(); } return null; } AID [] searchDF( String service ) { DFAgentDescription dfd = new DFAgentDescription(); ServiceDescription sd = new ServiceDescription(); sd.setType( service ); dfd.addServices(sd); SearchConstraints ALL = new SearchConstraints(); ALL.setMaxResults(new Long(-1)); try { DFAgentDescription[] result = DFService.search(this, dfd, ALL); AID[] agents = new AID[result.length]; for (i=0; i<result.length; i++) agents[i] = result[i].getName() ); return agents; } catch (FIPAException fe) { fe.printStackTrace(); } return null; }Running SearchDF2 with a few additional DF2 agents gives the following output:
jean% java jade.Boot bob:SearchDF2 alice:DF2 carole:DF2 Buyer: bob@Jeans-Computer.local.:1099/JADE Auction: not Found BUYERS: bob, alice, carole,
DFAgentDescription dfd = new DFAgentDescription(); ServiceDescription sd = new ServiceDescription(); sd.setType(....); dfd.addServices(sd); SearchConstraints sc = new SearchConstraints(); sc.setMaxResults(new Long(1)); send(DFService.createSubscriptionMessage(this, getDefaultDF(), dfd, sc));The DF will then send us INFORM messages in a standard FIPA format whenever an agent matching our description registers. We then need a behaviour to capture and decode those messages. For example:
addBehaviour(new HandleRegistrationMsg()); ....... class RegistrationNotification extends CyclicBehaviour { public void action() { ACLMessage msg = receive(MessageTemplate.MatchSender(getDefaultDF())); if (msg != null) { try { DFAgentDescription[] dfds = DFService.decodeNotification(msg.getContent()); if (dfds.length > 0) doSomething with dfds[0]... ; } catch (Exception ex) {} } block(); } // --- action --- }The API on the DFService gives a more compact way of doing this :
DFAgentDescription template = // fill the template addBehaviour( new SubscriptionInitiator( this, DFService.createSubscriptionMessage( this, getDefaultDF(), template, null)) { protected void handleInform(ACLMessage inform) { try { DFAgentDescription[] dfds = DFService.decodeNotification(inform.getContent()); // do something with dfds } catch (FIPAException fe) {fe.printStackTrace(); } } });
- Top
- Previous
- Next