Unleash mobile agents using Jini

Leverage Jini to mitigate the complexity of mobile agent applications

Mobile agents are ubiquitous in today's software applications—from e-commerce to network management to data warehousing. Mobile agent developers implement these solutions in Java for several reasons: First and foremost, Java's built-in object-oriented language features are conducive to agent technology. Second, developers can be extremely productive using Java. Essentially, Java provides tools that simplify and expedite complex software development tasks.

Jini is one such Java tool that allows us to build distributed applications with relative (and I emphasize relative) ease. This article introduces a mobile agent framework based on the Jini architecture. Jini provides a powerful tool in a Java developer's toolbox. Just as robots automate many aspects of manufacturing a computer, Jini automates and abstracts distributed applications' underlying details. These details include the low-level functionality (socket communication, synchronization, and so on) necessary to implement the high-level abstractions (such as service registration, discovery, and use) that Jini provides. Let's begin by looking briefly at agents.

Note: In this article, I do not introduce the Jini framework (see Resources for links to introductory material); I will just review the basics.


Agent is an overloaded industry buzzword—10 different gurus would give you 10 different definitions of the term. I prefer to talk about agents in terms of high-level functionality. I like the way Stan Franklin and Art Graesser view agents as discussed in their research paper "Is It an Agent, or Just a Program? A Taxonomy for Autonomous Agents" (1996). (Todd Sundsted references this paper in his article "An Introduction to Agents" (JavaWorld, June 1998), an article that makes good on its title's promise.) Instead of providing a rigid (and hence potentially restrictive) definition, Franklin and Graesser identify the following agent properties that enable a classification methodology:

  • Reactive
  • Autonomous
  • Goal-oriented
  • Temporally continuous
  • Communicative
  • Intelligent
  • Mobile

The authors note that an agent might not be a software entity at all, but possibly a robot or even a schoolteacher. In the context of this article and Java development, we can consider an agent a software entity that exhibits some combination of the previous properties.

What can we use mobile agents for? Mobile agents come in a variety of flavors and perform numerous functions:

  • An information agent searches for information residing on remote nodes and reports back to the source
  • A computation agent seeks underutilized network resources to perform CPU-intensive processing functions
  • A communication agent couriers messages back and forth between clients residing on various network nodes

Let's now look at a mobile agent framework that employs the Jini architecture.

Mobile agent framework

This mobile agent framework consists of two main components. The first component is the mobile agents themselves; that is, entities with some job to do. The second component is the mobile agent host(s), the service that provides the mobile agents' execution platform. In a distributed environment, we can have one-to-many agent hosts as well as one-to-many agents. To be an active agent platform, a given node in the system must have at least one active agent host. Figure 1 depicts the framework components.

Figure 1. Agent framework components

These two components map quite nicely to the Jini model. Jini, at the highest level, provides the infrastructure that enables clients to discover and use various services. Jini also provides a programming model for developers of Jini clients and services. In the context of this mobile agent framework, the agent host(s) provides Jini services. The mobile agent(s) is the Jini client.

Jini services register with one or more Jini lookup services by providing a service proxy for perspective clients. In turn, clients query the lookup service(s) for particular services that might be of interest. Figure 2 depicts that process.

Figure 2. Service registration and discovery. Click on thumbnail to view full-size image.

We will first look at building the agent host.

Agent host construction

The first step in building the agent host is to create a remote interface, the service template that agents will look for via the Jini lookup service. The AgentHostRemoteInterface provides one method, acceptAgent(), which agents call to travel to the implementing agent host:

public interface AgentHostRemoteInterface extends Remote {
   public void acceptAgent (AgentInterface ai) throws

The beauty of Jini is that objects can publish several interfaces—that is, provide multiple services. For instance, if we had a distributed data warehouse, we might have an agent host that provides a local data access service. In this instance, a data-mining agent might look for a host that provides the data access service and move to that host to perform localized mining operations. Therefore, we can have agents with different missions share hosts that provide multiple services.

The second step in building the agent host is to provide an implementation of this remote interface that is the actual Jini service. I have provided a MobileAgentHost class (see Resources to download complete code listings) that implements the AgentHostRemoteInterface.

Figure 3 shows the class diagram for MobileAgentHost.

Figure 3. Mobile agent host class diagram

The class extends the java.rmi.server package's UnicastRemoteObject class, which allows clients to obtain a remote reference and call its methods. The MobileAgentHost also implements the ServiceIDListener interface, which is passed a unique ServiceID object via the serviceIDNotify() method when the service first registers with a Jini lookup service. The MobileAgentHost constructor is shown below:

public class MobileAgentHost extends UnicastRemoteObject 
   implements AgentHostRemoteInterface, ServiceIDListener {
   private JoinManager myJoinManager = null;
   private ServiceID myServiceID = null;
   private LeaseRenewalManager myLeaseManager = null;
   private Object myAgentObject = null;
   private LookupDiscoveryManager myLDM = null;
   public MobileAgentHost(Object agentObject) throws IOException {
      myAgentObject = agentObject;
      // First parameter: Publish service to all groups.
      // Second parameter: Specifies a lookup locator 
      //     to perform unicast discovery at a known host/port
      //     (we don't use this, just multicast, which is automatic).
      // Third parameter: DiscoveryListener for callbacks when a lookup 
      //     service is discovered or discarded. This service
      //     registration will be handled by the JoinManager. 
      myLDM = new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS, 
         null, null);
      // First parameter: This service.
      // Second parameter: Attribute sets (none at this time).
      // Third parameter: ServiceIDListener implementor, which is notified 
      //     when a serviceID is assigned.
      // Fourth parameter: DiscoveryManagement implementor.
      // Fifth parameter: LeaseRenewalManager.
      myJoinManager = new JoinManager(this, null, this, myLDM, 
         new LeaseRenewalManager());

This MobileAgentHost constructor takes the agentObject object reference stored as member data and passed to arriving agents via the doWork() method. The constructor itself performs two basic functions. First, it creates a LookupDiscoveryManager to locate a Jini lookup service(s). Second, it creates a JoinManager, with the LookupDiscoveryManager as a parameter, to add this lookup service to the Jini service federation. As of Jini 1.1, the JoinManager simplifies service management by encapsulating the functionality required for both registering with and withdrawing from a dynamic lookup service pool.

In the acceptAgent() method's implementation shown below, the MobileAgentHost binds an incoming agent to an AgentThread:

public void acceptAgent(AgentInterface ai) throws RemoteException {
   AgentThread at = new AgentThread(ai);

In turn, an inner class instance, AgentThread, is created to run the bounded agent by calling its doWork() method, passing the LookupDiscoveryManager and agent object references (I'll discuss the doWork() method more in the following section):

private class AgentThread extends Thread {
   private AgentInterface myAgent = null;
   AgentThread(AgentInterface ai) {
      myAgent = ai;
   public void run() {
      myAgent.doWork(myLDM, myAgentObject);

In this implementation, a new thread is created for each arriving agent. You could enhance the implementation by binding incoming agents to an AgentThread from a pre-existing thread pool.

Agent construction

Now let's look at the mobile agents. The first step in building an agent is to create an interface for agents. For this, we create an AgentInterface that extends the Serializable interface. The Serializable interface marks the implementer as a serializable entity, or one that can be sent across the wire:

public interface AgentInterface extends Serializable {
   public void doWork(LookupDiscoveryManager ldm, Object workObject);

The AgentInterface consists of a doWork() method that is called when an agent arrives on a given host. This method takes two parameters. The first parameter is a reference to the LookupDiscoveryManager maintained by the current host. The agent uses this reference if and when it decides to look for new service providers, such as when it wants to travel to a new agent host. The second parameter is an optional (possibly null) object parameter, which contains data necessary for the agent to complete its job. For example, an agent that must communicate with other agents currently residing on this host might be passed a collection of agent references. The aforementioned data-mining agent might be passed a reference to a local database.

The second step in agent construction is providing an AgentInterface implementation -- for which I created an abstract MobileAgent class. This class's constructor builds a service template that locates services of type MobileAgentHostInterface. It also provides three additional methods: doWork(), moveToRandomHost(), and getMobileAgentHosts(), discussed below:

  1. To perform an agent-specific task, subclasses override the abstract doWork() method.
  2. When the agent wants to move, subclasses call the moveToRandomHost() method, which performs the following three steps:

    1. Gets a list of the currently available mobile agent hosts with a call to getMobileAgentHosts().
    2. Randomly selects a host from this list.
    3. Moves to a new host by calling the acceptAgent() method. If the call on the selected host fails, select a new host.
  3. To obtain a list of currently available agent hosts, subclasses call the getMobileAgentHosts(). This process requires the following steps:

    1. Call getRegistrars() to obtain a current list of lookup services.
    2. Iterate through each lookup service to find services that match the desired template; in this case, AgentHostRemoteInterfaces. Note that we might have duplicates—the same agent host registered with multiple lookup services. The myMAHServiceTemplate object, a ServiceTemplate class instance, passed to the lookup() method initializes in the MobileAgent constructor.
    3. Add each matching service to a vector of AgentHostRemoteInterfaces.

The following code illustrates these steps:

private Vector getMobileAgentHosts(LookupDiscoveryManager ldm) {
   // Find available lookup services
   ServiceRegistrar[] lookupServices = ldm.getRegistrars();
   // Find matching services (in our case, 
   // AgentHostRemoteInterfaces). 
   // Register with each lookup service.  
   Vector mobileAgentHosts = new Vector();
   ServiceMatches agentHosts = null;
   for (int i = 0; i < lookupServices.length; i++) {
      try {
         agentHosts =
      catch (RemoteException re) {
      for (int j = 0; j < agentHosts.totalMatches; j++) {
   return mobileAgentHosts;

Figure 4 depicts the interactions between the MobileAgent, MobileAgentHost, and the Jini lookup service.

Figure 4. Sequence diagram for interactions between the MobileAgentHost, MobileAgent, and Jini lookup service

The final step in building the agent: create a concrete mobile agent implementation. I chose to implement a RouteMapAgent that extends MobileAgent. This agent randomly travels to various agent hosts and logs its route. In this implementation, the object passed to the doWork() method by the AgentHost is the local hostname. The agent records this name in its route map. Figure 5 depicts the RouteMapAgent class diagram.

Figure 5. RouteMapAgent class diagram. Click on thumbnail to view full-size image.

RouteMapAgent's doWork() method, called when an agent arrives at a host, is implemented as follows:

  1. Display the route taken to get to this host
  2. Record the current host in the route table
  3. Search for and travel to a new agent host (call moveToRandomHost())

The code below demonstrates these steps:

1 2 Page 1
Page 1 of 2