Discovery

Basically, the Directory Facilitator agent (DF) is a yellow pages server. Provider agents may register (and deregister) the types of services they perform with the DF. Client agents may search the DF for providers of a given service.

The interface to the DF agent is through the DFService utility class. We also provide a DFUtils class.

ProviderAgent.java

Our provider agent has a single provider behavior that performs two distinct services called "ServiceA" and "ServiceB":

class ProviderBehavior extends CyclicBehaviour {

   protected ACLMessage makeReply(ACLMessage msg) {
      String info = msg.getContent();
      ACLMessage reply = msg.createReply();
      if (info.equals("ServiceA")) {
         reply.setContent("Performing serviceA for " +
            msg.getSender().getLocalName());
      } else if (info.equals("ServiceB")) {
         reply.setContent("Performing serviceB for " +
            msg.getSender().getLocalName());
      } else {
         reply.setContent("What?");
      }
      return reply;
   }

   public ProviderBehavior(Agent a) { super(a); }

   public void action() {
      ACLMessage msg = myAgent.receive();
      if (msg != null) myAgent.send(makeReply(msg));
      block();
   }
}

Of course we could have created two separate behaviors, one for each service.

During setup, the provider agent creates a DFD (DFAgent description record). The DFD will contain the AID of the agent plus information about the protocols, ontologies, languages, and services the agent supports:

DFAgentDescription dfd = new DFAgentDescription();
dfd.setName(getAID());

For each service the agent performs, we must create a service description and add it to the DFD. For example:

ServiceDescription serviceA  = new ServiceDescription();
serviceA.setType( "serviceA" );
serviceA.setName( getLocalName() );

Before registering the DFD, we must deregister previous incarnations:

DFAgentDescription list[] = DFService.search( this, dfd );
if ( list.length > 0 ) DFService.deregister(this);

Now we are ready to register the DFD:

DFService.register(this, dfd);

During takedown the provider agent must deregister itself:

DFService.deregister(this);

Here's the complete code:

public class ProviderAgent extends Agent {

   protected void setup() {

      DFAgentDescription dfd = new DFAgentDescription();
      dfd.setName(getAID());

      ServiceDescription serviceA  = new ServiceDescription();
      serviceA.setType( "serviceA" );
      serviceA.setName( getLocalName() );

      ServiceDescription serviceB  = new ServiceDescription();
      serviceB.setType( "serviceB" );
      serviceB.setName( getLocalName() );

      try {
        DFAgentDescription list[] = DFService.search( this, dfd );
         if ( list.length > 0 ) DFService.deregister(this);
         dfd.addServices(serviceA);
         dfd.addServices(serviceB);
         DFService.register(this, dfd);
      } catch (FIPAException fe) {
         System.err.println(fe.getMessage());
      }

      addBehaviour(new demos.ProviderBehavior(this));
      System.out.println("Agent " + getLocalName() + " is ready!");
  }

  protected void takeDown() {
     try {
      DFService.deregister(this);
     } catch (FIPAException fe) {
        System.err.println(fe.getMessage());
     }
     System.out.println("Agent " + getLocalName() +
         " is shutting down");
  }
}

RequestAgent.java

Our request agent has two behaviors, one that sends a "ServiceA" request to all agents that provide ServiceA, and another that sends a "ServiceB" request to all agents that provide ServiceB. The RequestB behavior searches the DF for all agents that provide "ServiceB":

AID[] providers = DFUtils.searchDF(myAgent, "ServiceB");

To send a request for "ServiceB" to provider i is simple:

ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
msg.setContent( "ServiceB" );
msg.addReceiver( providers[i]);
myAgent.send(msg);

Here's the complete code:

class RequestBBehavior extends OneShotBehaviour {
   public RequestBBehavior(Agent a) { super(a); }
   public void action() {
      AID[] providers = DFUtils.searchDF(myAgent, "ServiceB");
      if (providers != null) {
         for(int i = 0; i < providers.length; i++) {
             ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
             msg.setContent( "ServiceB" );
             msg.addReceiver( providers[i]);
             myAgent.send(msg);
             msg= myAgent.blockingReceive();
             if (msg!=null)
               System.out.print("Response: " +  msg.getContent());
               System.out.println(" from " + 
                  msg.getSender().getLocalName() );
         } // for
      } // if
   }
}

Here's the code for the request agent:

public class RequestAgent extends Agent {

    protected void setup() {
       addBehaviour(new demos.RequestABehavior(this));
       addBehaviour(new demos.RequestBBehavior(this));
    }
}

DFUtils.java

public class DFUtils {
   static void register( Agent a, ServiceDescription sd) { ... }
   static AID getService( Agent a, String service ) { ... }
   static AID [] searchDF( Agent a, String service ) { ... }
}

Single Service Registration

   static void register( Agent a, ServiceDescription sd) {
      DFAgentDescription dfd = new DFAgentDescription();
      dfd.setName(a.getAID());

        try {
        DFAgentDescription list[] = DFService.search( a, dfd );
         if ( list.length>0 )
               DFService.deregister(a);

            dfd.addServices(sd);
         DFService.register(a, dfd);
      }
        catch (FIPAException fe) {
         fe.printStackTrace();
      }
   }

Search for a single provider of a service

   static AID getService( Agent a, String service ) {
      DFAgentDescription dfd = new DFAgentDescription();
         ServiceDescription sd = new ServiceDescription();
         sd.setType( service );
      dfd.addServices(sd);
      try
      {
         DFAgentDescription[] result = DFService.search(a, dfd);
         if (result.length>0)
            return result[0].getName() ;
      }
        catch (FIPAException fe) {
         fe.printStackTrace();
      }
         return null;
   }

Search for all providers of a service

   static AID [] searchDF( Agent a, 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(a, dfd, ALL);
         AID[] agents = new AID[result.length];
         for (int i=0; i<result.length; i++)
            agents[i] = result[i].getName() ;
         return agents;

      }
        catch (FIPAException fe) { fe.printStackTrace(); }

         return null;
   }

Program output

In this example larry, curly, and moe are provider agents and abbot is a request agent:

Agent larry is ready!
Agent curly is ready!
Agent moe is ready!
Response: Performing serviceA for abbot from moe
Response: Performing serviceA for abbot from curly
Response: Performing serviceA for abbot from larry
Response: Performing serviceB for abbot from moe
Response: Performing serviceB for abbot from curly
Response: Performing serviceB for abbot from larry

Adding containers

We can start new containers in separate console windows using the command:

runjade -container

To start a container on a separate machine, we must specify the URL of the main container:

runjade -container -host myServer.cs.sjsu.edu

Of course once the container has started, it appears in the GUI and we can add agents to it:

Alternatively, we can add the agent at the command line:

runjade -container -host myServer.cs.sjsu.edu sue:demos.RequestAgent