Agent Mobility with JADE and JIPMS

A friend and I have been working on Java Agent DEvelopment Framework (JADE) for a while now. The idea is to enhance security mechanisms in the open source agent-deployment platform. The first step we decided to address was the actual mobility of an agent from one platform (in the sense of a dedicated machine running the JADE middleware) to another one. Turned out that it was much harder than one would imagine -- especially given the fact that these agents are supposed to be mobile. Anyway, after around two months of part-time efforts, we got the agent working. Since the whole ordeal involved a lot of missing documentation and bad support, I decided to document the process through this tutorial. So, here it is. Read on to see how you can create an agent on one platform, migrate it to another platform, have it do some computation there and come back to the source. 

First, you're going to need two machines running Ubuntu. (We used Ubuntu 11.10 oneric for this.) If you only have one machine, you can install VirtualBox, setup a Ubuntu instance in a VM and connect it to the host through a virtual interface. I prefer using two adapters, first one set to a NAT setting and another one set to Host-only setting. This way, I get Internet connectivity in the guest as well as host-to-guest simple addressing.

We're going to use JADE 3.6 along with the JADE Inter-platform Mobility Service (JIPMS) 1.2 for our needs. Download JADE and JIPMS.  I used jade-binary package and extracted it in /home/documents/jade. I also extracted the JIPMS and moved the file migration.jar from its lib folder to the lib folder of jade binary. This makes it easier for us to change the classpath later on. We'll need these files on both machines.

Now we need to setup the machines. Here are the details of the setup on each machine:

Host:

hostname: slave1
IP: 192.168.56.1 (set by VirtualBox automatically)

You can get the IP addresses of each machine through the ifconfig command. Edit /etc/hosts to insert the other machine's address. My hosts file on slave1 looks like this:

[sourcecode language="bash"]
# 127.0.0.1   localhost
127.0.0.1     slave1
192.168.56.101    slave2
[/sourcecode]

Now, we can run jade using the following command:

[sourcecode language="bash"]
java -cp lib/jade.jar:lib/migration.jar:lib/iiop.jar:
lib/jadeTools.jar:lib/http.jar:
lib/commons-codec/commons-codec-1.3.jar
jade.Boot
-gui
-platform-id slave1
-host slave1
-services jade.core.mobility.AgentMobilityService;
jade.core.migration.InterPlatformMobilityService
-accept-foreign-agents true
[/sourcecode]

You will need to use a little common sense about the line breaks and spaces here. I've formatted the command for highest readability. (Note the escaped ';' in the services param and the use of full colon instead of the semi-colon in the classpath. This is only required on *nix platforms.) Three switches are important here: platform-id, host and services.  First two are self-explanatory. Third tells the IPMS to start the services that take care of the agent migration. We also need to enable the acceptance of foreign agents but I'm sure you already knew that from all the mailing list searches.

Guest:

hostname: slave2
IP: 192.168.56.101 (set by VirtualBox automatically)

The hosts file looks like this:

[sourcecode language="bash"]
# 127.0.0.1   localhost
127.0.0.1      slave2
192.168.56.1    slave1
[/sourcecode]

Start jade on slave2 using the following command:

[sourcecode language="bash"]
java -cp lib/jade.jar:lib/migration.jar:lib/iiop.jar:
lib/jadeTools.jar:lib/http.jar:
lib/commons-codec/commons-codec-1.3.jar
jade.Boot
-gui
-platform-id slave2
-host slave2
-services jade.core.mobility.AgentMobilityService;
jade.core.migration.InterPlatformMobilityService
-accept-foreign-agents true
[/sourcecode]

The Agent:

Now, we turn to the actual agent code that does the migration. For this, we can setup eclipse and code from within that. The code for the agent is fairly straight-forward:

[sourcecode language="java"]
package org.csrdu.mobility;

import jade.core.AID;
import jade.core.Agent;
import jade.core.PlatformID;
import jade.core.behaviours.TickerBehaviour;

@SuppressWarnings("serial")
public class MobileAgent extends Agent {

@Override
protected void setup() {
super.setup();
addBehaviour(new MyTickerBehaviour(this, 1000));

System.out.println("Hello World. I am an agent!");
System.out.println("My LocalName: " + getAID().getLocalName());
System.out.println("My Name: " + getAID().getName());
System.out.println("My Address: " + getAID().getAddressesArray()[0]);
}

private class MyTickerBehaviour extends TickerBehaviour {
Agent agent;
// long interval;
int counter;

public MyTickerBehaviour(Agent agent, long interval) {
super(agent, interval);
this.agent = agent;
// this.interval = interval;
}

@Override
protected void onTick() {
if (counter == 3) {
// move out
AID remoteAMS = new AID("ams@slave2", AID.ISGUID);
remoteAMS.addAddresses("http://slave2:7778/acc");
PlatformID destination = new PlatformID(remoteAMS);
agent.doMove(destination);
}
if (counter == 10) {
// move back
AID remoteAMS = new AID("ams@slave1", AID.ISGUID);
remoteAMS.addAddresses("http://slave1:7778/acc");
PlatformID destination = new PlatformID(remoteAMS);
agent.doMove(destination);
}
if (counter < 15)
System.out.println(counter++);
else
agent.doDelete();
}

}
}
[/sourcecode]

For the sake of completion, here's the arguments that you have to pass when running the agent code from within eclipse.

[sourcecode]
-container -agents mob:org.csrdu.mobility.MobileAgent
-services jade.core.mobility.AgentMobilityService;jade.core.migration.InterPlatformMobilityService
[/sourcecode]

Also, add all the jade and mobility jars to the build path of the project. I'm not sure which ones are needed here so you will have to figure that out on your own.

Caveats:

  1. If you get this error: Destination slave2:1099/JADE does not exist or does not support mobility, it most probably means that you (a) you don't have IPMS running, (b) you didn't put the -services switch properly (c) there's a firewall blocking your port 7778 or (d) your hostnames are messed up. In any case, the whole instructions above should work for you.
  2. You will most probably need to change your /etc/hosts file and comment out the line that says '127.0.0.1 localhost'. It causes JADE to start the ams@slaveN service as http://localhost:7778/acc instead of http://slaveN:7778/acc and that means a lot of missed ACL messages and a lot of headaches. You usually get the dreaded getContainerID() failed to find agent ams@slave1 error because of this. Do this for both the host and the guest. Make sure your JADE GUI looks like the following:
  3. Finally, if you need detailed logging, you can create the logging config file by the name of logging.properties given below and pass the -Djava.util.logging.config.file=logging.properties parameter when starting JADE. This will give much finer-grained logs. This is standard log4j syntax.

[sourcecode]
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
.level=INFO
jade.domain.mobility.level=ALL

# --- ConsoleHandler ---
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

# --- FileHandler --- java.util.logging.FileHandler.level=ALL java.util.logging.FileHandler.pattern=%h/java%u.log java.util.logging.FileHandler.limit=50000 java.util.logging.FileHandler.count=1 java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter [/sourcecode]