Channels ▼
RSS

JVM Languages

Java Mobile Agents & the Aglets SDK

Source Code Accompanies This Article. Download It Now.


Jan02: Java Mobile Agents & the Aglets SDK

Migratory software for distributed systems

Tim is a software engineer with a background in the development of embedded networked systems. He can be contacted at mtj@mtjones.com.


Mobile agents, sometimes referred to as "migratory software," aren't new. In fact, both the concept and a number of implementations have existed since the early 1980s, when operating-system researchers implemented architectures whereby processes could migrate between homogeneous systems. This migration embodied not only the process code, but also its data. The primary use of process migration was for load balancing.

In the early 1990s, General Magic introduced the Telescript mobile agent framework and with it the term "Mobile Agent." Numerous mobile agent frameworks followed Telescript using languages such as Java, TCL/Tk, and Python.

In this article, I discuss the development of Java mobile agents on Linux using the Aglets framework (http://www.trl.ibm.com/aglets/ and http://www.aglets.org/). I then present a mobile agent that migrates to a number of hosts and performs data collection along the way.

Why Are Mobile Agents Important?

Mobile agents (MA) provide an interesting approach to solving certain classes of problems. For instance, consider data collection problems where the data is remotely located. Mobile agents can migrate to the remote nodes to perform the data collection and then reduce the collected data via filtering or compression. In this way, network bandwidth savings are realized. For remote databases, mobile agents can encapsulate queries and bundle the return of the results. This may sound like a problem easily solved by a simple client/server architecture, but what happens if the nodes are regularly disconnected from the network? In the typical client/server architecture, once the client goes down, the results are retried and then dropped by the server. In the MA model, mobile agents wait at the server until the client is available, then migrate back to provide their results.

Another promising application domain involves extensible servers, where users migrate mobile agents to a remote server that represents them in terms of personalization. The remote agent then contacts users whenever anything occurs that may be of interest to them.

A final example is the dynamic deployment of software. Consider an environment made up of laptop and palmtop computers. Ensuring that all have up-to-date software is problematic, and their disconnected nature makes it difficult for administrators to collectively manage them. If applications are bundled within mobile agents, then the agent portion of the application can keep in touch with a remote server to ensure proper version consistency while the bundled application performs its intended function at the remote host.

Linux and the Aglets SDK

Developed by IBM's Tokyo Research Lab and freely available at http://www.trl.ibm.com/aglets/download.html, the Aglets SDK is an open-source SDK for Java 1.1. The Aglets SDK contains Java classes to build aglets, documentation (in HTML format), source examples of aglets, and the Tahiti Aglets Server. When you've downloaded the SDK, you can use Example 1(a) to install the SDK, creating the subdirectory /usr/local/src/aglets1.1.0. To configure the Aglets SDK, you "cd" into the ./aglets1.1.0/bin subdirectory and edit the agletsd shell script. This script starts the Tahiti Aglets Server that is useful to start and debug aglets. At the beginning of the script, update the JDK_HOME symbol to point to your JDK subdirectory and the AGLET_HOME symbol to point to your aglet distribution. Example 1(b) shows how I configured my file.

This release of the Aglets SDK requires a JDK 1.1.8 that can be downloaded from Sun for Windows (http://java.sun.com/), and for Linux, from the Blackdown site (http://www.blackdown.org/) for your particular architecture. I've used the JDK 1.1.6, but experienced problems when mixing the two versions. Consequently, I advise sticking with JDK 1.1.8.

Finally, to start an aglet server, execute the agletsd script you previously modified (./agletsd). This first asks you to enter a user name and password for the server. Once this information is reconfirmed, the Tahiti Aglets Server window appears. From this window, you can create aglets, dispatch them to remote nodes, and bring them back and delete them. Log utilities also exist to monitor server activity and memory usage of the server.

Mobility

What gives aglets the ability to migrate within a network is the Agent Transport Protocol (ATP) — the conduit that communicates an agent and its state to a remote server. For mobile agents to migrate themselves autonomously requires the creation of a "ticket." Tickets are created with a destination, and using the dispatch call with a newly created ticket serializes and then migrates the aglet to the destination. Upon arriving at the remote server, the aglet is restarted with its data intact; see Example 2(a). All data within the aglet is also transferred, allowing the aglet to be statefully driven. Migrating all data with the aglet may be unnecessary because some data may only be used for location operations. In these cases, you can declare an object as transient, as in Example 2(b). When the aglet is migrated, the instance of this object is left behind and created new at the destination.

Sample Problem

To illustrate, I now present a simple application where a mobile agent visits a number of nodes and collects data at each one. When the agent returns to the originating host, a Java AWT window is presented with the data collected along the way.

The collector class in Listing One is based on the Aglets class that provides a rich set of functions for both mobility and communication. Listing Two is a makefile for building the aglet.

The first method in the collector class is a callback that lets the Aglets framework notify you when important events occur. In this case, you want to know when your aglet is first created (via the OnCreation callback). In this method, you create objects to be used during the life of this aglet. Also, in this method, you register the desire to receive events when the aglet arrives at a node via the OnArrival callback. You use this method to perform data collection upon arriving at a node.

The getCurrentNodeInfo and parseItem methods gather data at the node and parse information from the Linux proc filesystem. For data gathering, I use the System class to gather OS/JVM information and the Linux proc filesystem to gather network throughput information.

The final method, run, is invoked each time the aglet is started on a node. So when it is initially created, the run method is called. When the aglet migrates to another node, the run method is called again. Therefore, you create some state information to let us know what you're doing. This simple state machine is performed sequentially, and is used to migrate to my "Alto" node (192.168.1.5), back to the originating node "Plato" (192.168.1.7), and then finally to emit the data to the user; see Figure 1. Each hop is performed using the ticket/dispatch method.

The resultsWindow class extends Frame to provide a simple TextArea to display the collected information to users.

The log window (available through the Tahiti Aglets Server) provides useful information for debugging aglets.

IBM's Aglets framework also provides a number of other classes that simplify migration to a number of hosts. Aglets may define an itinerary that specifies a number of hosts to visit, and a message to send to the aglet when arriving at a particular host. In this way, the state machine is replaced with a host/message pair and no state data is necessary at the aglet level. As the aglet visits each host, the unique message is sent from the framework to the aglet that in turn drives the aglet to perform whatever activities were necessary at the node.

The Future

A current interesting area that may benefit from mobile agents is the deployment of dynamic services to remote devices. The Open Service Gateway Initiative (OSGI) provides a Java framework for the distribution of Java services to remote nodes and shares some of the same technologies that have existed within mobile agent frameworks for some time.

One interesting application is the encapsulation of network services within mobile agents. The mobile agent provides the migration and installs accounting and a service onto a remote node. The mobile agent can determine, prior to install, if the remote environment is satisfactory for the service (all necessary services are available to it). Once installed, the mobile agent deploys the service and then monitors it at the remote location. This is similar to the analogy of a technician coming to your office to install a piece of equipment. But after the equipment is installed, the technician remains to monitor the equipment, communicate with the office for reconfiguration, or recall.

Conclusion

Mobile agent frameworks aren't a panacea for the growing complexity in our networked systems designs, but they do provide another way to solve certain classes of problems and can be a useful architectural addition to our design solutions toolbox.

DDJ

Listing One

// collector.java - Example Aglet.
// M. Tim Jones <mtj@mtjones.com>
import com.ibm.aglet.*;
import com.ibm.aglet.event.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;

public class collector extends Aglet {
  StringBuffer buffer;
  int stage = 0;
  //--------------------------------------------------------------------
  // onCreation()
  public void onCreation(Object ini) {
    buffer = new StringBuffer();
    buffer.append("Collected Information:\r\n\r\n");
    addMobilityListener(new MobilityAdapter() {
      public void onArrival(MobilityEvent e) {
        getCurrentNodeInfo();
      }
    });
  }
  //--------------------------------------------------------------------
  // getCurrentNodeInfo()
  private void getCurrentNodeInfo()
  {
    BufferedReader inReader;
    String line;
    String iface;
    buffer.append( "  Node " + 
                    getAgletContext().getHostingURL().toString()+" - \r\n");
    buffer.append( "    OS : " + 
                    System.getProperty("os.name") + " / " +
                    System.getProperty("os.version") + " / " +
                    System.getProperty("os.arch")+"\r\n" );
    buffer.append( "    Java Vers : " + 
                    System.getProperty("java.version")+"\r\n" );
    try {
      inReader = new BufferedReader(new FileReader("/proc/net/dev"));
      do {
        line = inReader.readLine();
        if (line != null) {
          iface = line.substring(0, 6).trim();
          if (iface.compareTo("eth0") == 0) {
            buffer.append(  "    " + line.substring(0, 6).trim() + ":" + 
                            "   Rx Bytes : " + parseItem(line, 1) + 
                            "   Tx Bytes : " + parseItem(line, 9) + "\r\n");
          }
        }
      } while (line != null);
    } catch (Exception excpt) { 
      excpt.printStackTrace();
    }
    buffer.append("\r\n");
  }
  private String parseItem(String line, int number) {
    int retValue = 0, startIndex = 7, endIndex = 0;
    if ((line != null) && ( number > 0)) {
      while (line.charAt(startIndex) == ' ') startIndex++;
      number--;
      while (number-- != 0) {
        // Find the start of a numeric entry
        while (line.charAt(startIndex) == ' ') startIndex++;
        // Skip a number
        while (line.charAt(startIndex) != ' ') startIndex++;
        while (line.charAt(startIndex) == ' ') startIndex++;
      }
      endIndex = startIndex + 1;
      // Find the end of a numeric entry
      while (line.charAt(endIndex) != ' ') endIndex++;
      return(line.substring(startIndex, endIndex));
    }
    return(null);
  }
  //--------------------------------------------------------------------
  // run()
  public void run() {
    // Define a simple Quality of Communication
    QoC qoc = new QoC(QoC.NORMALINTEGRITY, QoC.NOCONFIDENTIALITY);
    try {
      if (stage == 0) {
        Ticket ticket = new Ticket("atp://192.168.1.5/", qoc);
        if (ticket == null) {
          System.out.println("Couldn't get ticket to alto");
          System.exit(-1);
        }
        try {
          stage = 1;
          dispatch(ticket);
        } catch(Exception excpt) {
          excpt.printStackTrace();
        }
      } else if (stage == 1) {
        Ticket ticket = new Ticket("atp://192.168.1.7/", qoc);
        stage = 2;
        if (ticket == null) {
          System.out.println("Couldn't get ticket to plato");
          System.exit(-1);
        }
        try {
          dispatch(ticket);
        } catch(Exception excpt) {
          excpt.printStackTrace();
        }
      } else if (stage == 2) {
        resultsWindow window = null;
        window = new resultsWindow();
        window.appendText(buffer);
        window.pack();
        window.show();
        stage = 3;
      }
    } catch(MalformedURLException excpt) {
      excpt.printStackTrace();
      System.exit(-1);
    }
  }
}
class resultsWindow extends Frame implements ActionListener {
  TextArea text = new TextArea();
  public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
  }
  public resultsWindow() {
    super("Results");
    setLayout(new BorderLayout(5, 5));
    add("Center", text);
    text.setEditable(false);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        setVisible(false);
      }
    });
  }
  public void appendText(StringBuffer str) {
    text.append( str.toString() + "\r\n");
  }
}

Back to Article

Listing Two

# Makefile
JDK_HOME = /usr/java/jdk118_v1
JAVAC = $(JDK_HOME)/bin/javac
CLASSPATH = /usr/local/src/aglets1.1.0/src:$(JDK_HOME)/lib/classes.zip

AGLETSLIBPATH = /usr/local/src/aglets1.1.0/lib
AGLETSPUBPATH = /usr/local/src/aglets1.1.0/public

JAVACPUB = $(JAVAC) -deprecation -classpath $(AGLETSLIBPATH):
                        $(AGLETSPUBPATH):$(CLASSPATH) -d $(AGLETSPUBPATH)
migtest:
    $(JAVACPUB) collector.java

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video