Table of Contents
This chapter tells you how to create agents It will show you the steps needed to build your first agent, show you how to use the DNAComposer (a command line tool that creates a DNA file). Finally we introduce the Deploy Tool, a gui which helps developers gconfigure and deploy their agents.
This chapter explains the various steps and gives examples on how to use the ADK functions. Additionally, refer to the Template project which is included with the ADK and further described in Section 3.4, “The Agent Template”. It provides a structure that you can use to get started with creating your own agent and requires ANT[3] to create, compile, package and run agents. Additionally, the ADK Scenarios document describes several agent examples that will help guide you through more features of the ADK.
After doing system checks, setting the CLASSPATH, etc. in your preferred IDE, you will be ready to compile your first agent. If you are not familiar with Java, refer to the many tutorials that teach programming. A good primer can be found online at the Sun website: http://java.sun.com/docs/books/tutorial
You may have already seen the source of the HelloAgent in the QuickStart Guide. In this section, you will be shown how to create your own agent using the source code of the Hello World Agent. Instructions on how to start up the examples are also found in the ADK Scenarios document.
The afc.jar and are.jar files contain the classes you need to extend to create your own agents and tasks. They are located in the lib directory. Put these in your CLASSPATH (in your IDE or as an environment variable of your system). The CLASSPATH will only be used for compiling, not for running the habitat.
To make a simple agent you have to create two java files, one for the agent and one for its task. First create MyHelloAgent.java, this should extend tryllian.afc.agent.Agent, and then, MyHelloTask.java which should extend tryllian.afc.task.DefaultTask. For ease of use put these files in your own package structure, based on the examples in the \src directory, for example examples\src\yourpackage\agents. (You can take a look at the HelloWorld source, which can be found in examples\src\tryllian\examples\adk\hello).
Now you are ready to compile the two classes (if you don't use an IDE, you need to set your CLASSPATH and compile using javac yourpackage\MyHelloAgent.java yourpackage\MyHelloTask.java). After compiling your classes, you need to package them in a jar file. You will need to package your class files and put the jar in the examples\lib directory, next to the example jars. To package them enter:
jar cf my.jar yourpackage\MyHelloAgent.class yourpackage\MyHelloTask.class.
Agents are packaged into DNA files. The DNA file is a file that contains:
The jar file of your agent
The optional libraries that your agent uses
A file descriptor that contains the location of the class file of your agent in the jar file and the location of the libraries needed
The signature of the agent with your private key for authentification and versioning
If your agent moves to another habitat, only the files that need to be updated will be transferred through the network. If your agent or a similar agent has already moved to the remote habitat only the state of the agent is transferred.
First, you need to create a special XML file called the agent descriptor. Here is the code:
<agentdescriptor>
<target target-id="standard">
<environment>
<java areversions="1.3"
agentclass="yourpackage.MyHelloAgent"
/>
</environment>
<file name="my.jar"/>
<shared-ref type="stdlib"
name="afc"
/>
</target>
</agentdescriptor>
You can now create your DNA file using the DNAComposer (For more information see Section 5.7, “DNA Composer”) that is in the bin directory:
dnacomposer --create --file my.dna --descriptor file-desciptor.xml --jarpath .
--keystore config\keystore.dat --alias developer1 --password developer1
Now you have created a DNA file. For more information on signing and keystores see Chapter 12, Security.
To start a habitat for the agent you need to create a platform. To create a habitat, you need to create an XML file similar to the ones in the examples directory. Specify your agent class as the agent and your jar file as the jar file. Use helloworld.xml as an example and modify the agent tag to load your agent. The agent tag in the modified file would look like this (take a look at the note about location of DNA files):
<agent dna="path/to/my.dna">
</agent>
Save the XML file as myagent.xml. More information on the format of a habitat xml definition file can be found later in this chapter (Section 5.5.1, “Format of the Habitat XML file”)
The location of the DNA files is resolved relative to the path set in classloader.basedir specified in the habitat.properties file. In the ADK distribution, this property is normally set to the ADK_ROOT directory. In the agent template and kickstart (sample project), this property is set to the basedir of these projects.
Setting the property to "." will result in the locations being resolved against the startup directory of the habitat. If your habitat gives the error that it is unable to find the DNA file of an agent, check the setting of classloader.basedir and the path specified in the habiat XML file. Together, these should point to the correct location. This behavior will probably change in future releases.
Now you are ready to run your first agent, enter
habitat myagent.xml
The output should look similar to:
tainer:~/src/tryllian/adk-3.0b3/scenarios boud$ ./habitat.sh helloworld.xml Tryllian's Agent Habitat (C) Copyright Tryllian Solutions B.V. 2005 ADK v3.0 Using /Users/boud/src/tryllian/adk-3.0b3 as logging directory. Remote plugins allowed: false Starting plugin: com.tryllian.messenger.jxta.JXTAMessengerPlugin Primary name of this host: tainer.local Primary address of this host: 172.16.1.8 tainer.local resolves to 172.16.1.8 172.16.1.8 resolves to 172.16.1.8 *** Resolved primary IP address does not match primary host name *** *** Potential problem detected *** *** Consult the developers guide *** Attempting to guess my IP-address .! Guessed my IP: 172.16.1.8 But this computer has a defective network configuration. NetConfig Hostname: 172.16.1.8 NetConfig IP Address: 172.16.1.8 Registering: com.tryllian.messenger.jxta.JXTAMessengerPlugin[RemoteStub [ref: [endpoint:[172.16.1.8:49281](local),objID:[1]]]] Habitat ID: 60000003-0000c350bec7fbd4 Habitat name: tainer ----------------------------------------- Restored 0 checkpointed agents and created 1 agent from habitat XML file. [hello-agent] Hello World
You have now compiled your first agent! Extending it should not be difficult, take a look at the XML files and the source code. Basic functionality is explained below.
Mobility is an important feature of the ADK. To demonstrate this movement, the following examples are provided.
Mobility is one of the most essential assets of an agent. Without it, an agent would not be able to carry out its assigned tasks at remote locations.
An agent on a mission would typically perform the following actions:
Leave present location and go to another location.
Perform tasks at one or more remote locations.
Return to home location.
First, we will create an agent that prints a message in the habitat where it is located, then moves to another habitat and prints another message.
The following code shows how to implement such a transport strategy. It is similar to the HelloAgent code. However, the MovingHelloAgent has the MovingHelloTask assigned to it instead of the HelloAgentTask.
package tryllian.examples.afc.move;
import tryllian.afc.agent.Agent;
/**
* Agent with a MovingHelloTask.
* @see MovingHelloTask
*/
public class MovingHelloAgent extends Agent
implements tryllian.are.TransientAgent
{
private final String destinationHabitat;
/**
* Creates a MovingHelloAgent.
*/
public MovingHelloAgent(String[] arguments) {
if (arguments.length != 2) {
System.err.println("[MoveAgent]: Two arguments are needed:");
System.err.println("[MoveAgent]: 1) agent name");
System.err.println("[MoveAgent]: 2) destination habitat");
}
setName(arguments[0]);
destinationHabitat = arguments[1];
}
public void agentStarted() {
addTask(new MovingHelloTask(destinationHabitat));
}
}
Here is the more interesting MovingHelloTask. This task uses a special MoveTask that provides the mobility needed to perform the requested tasks.
The MoveTask works by signaling the ARE that the agent wants to move to another location. The system takes care of the movement when notified.
package tryllian.examples.afc.move;
import tryllian.afc.message.*;
import tryllian.afc.task.DefaultTask;
import tryllian.afc.task.TaskEvent;
import tryllian.afc.task.TaskListener;
import tryllian.afc.task.standard.MoveTask;
/**
* Simple task that takes the following steps:
*
* Prints a message to System.out;
* Moves to a different habitat
* Prints another message to System.out
*
*/
public class MovingHelloTask extends DefaultTask implements TaskListener {
// State
private static final int JUST_STARTED = 0;
private static final int TRYING_TO_MOVE = 1;
private static final int HAS_MOVED = 2;
private final String destination;
private int status = JUST_STARTED;
private MoveTask moveTask;
public MovingHelloTask(String destination) {
this.destination = destination;
}
public void handleHeartbeat() {
// Split behavior into cases 'at home' and 'away'.
switch (status) {
case JUST_STARTED:
reasonAtHome();
break;
case HAS_MOVED:
reasonAway();
break;
}
}
private void reasonAtHome() {
// Print a message and try to move.
println("Hello Agent World!");
println("Trying to move to " + destination);
moveTask = new MoveTask(destination);
moveTask.addTaskListener(this);
addTask(moveTask);
// Change state
status = TRYING_TO_MOVE;
}
private void reasonAway() {
// Print a message and succeed task
println("Hello Brave New World!");
succeed();
}
/**
* Called when MoveTask is started.
*/
public void taskStarted(TaskEvent event) {}
/**
* Called when MoveTask is ended.
*/
public void taskEnded(TaskEvent event) {
if (moveTask.getState().isSucceeded()) {
status = HAS_MOVED;
}
else {
println("Could not move; failing task!");
fail();
}
}
private void println(String message) {
System.out.println(
"[MoveAgent in "
+ getAgentContext().getMyAddress().getHabitatName()
+ "] "
+ message);
}
}
The MovingHelloTask has different behavior depending on its state. If it is at home, its state is JUST_STARTED and it will then reason further in the method reasonAtHome() which tries to let the agent to depart. If the state is TRYING_TO_MOVE nothing is done, because reasonAtHome() already has been called and the agent is waiting to get transported. When the state is equal to HAS_MOVED, the task will then start reasonAway(), because the agent has been moved and it performs another kind of behavior than it does when it's at home.
If the agent is at home, it tries to move to the second habitat, it will update the status variable so that the handleHeartbeat() method is able to see that the agent is trying to move. This prevents multiple move calls.
private void reasonAtHome() {
// Print a message and try to move.
println("Hello Agent World!");
println("Trying to move to " + destination);
moveTask = new MoveTask(destination);
moveTask.addTaskListener(this);
addTask(moveTask);
// Change state
status = TRYING_TO_MOVE;
}
The agent can be moved by adding a MoveTask. The MoveTask is successful when the agent has moved.
Once the MovingHelloTask has requested the move task to be executed, we need to know when the task has ended, i.e., when the agent has moved. The MovingHelloTask will register itself as a listener to MoveTask. That's why the MovingHelloTask implements TaskListener. Now taskStarted is called when MoveTask starts executing and taskEnded when it has finished. Concluding in receiving a call in taskEnded when the agent has moved.
/**
* Called when MoveTask is started.
*/
public void taskStarted(TaskEvent event) {}
/**
* Called when MoveTask is ended.
*/
public void taskEnded(TaskEvent event) {
if (moveTask.getState().isSucceeded()) {
status = HAS_MOVED;
}
else {
println("Could not move; failing task!");
fail();
}
}
}
When MoveTask has successfully completed, it means that the agent has successfully moved to the desired location. Its state has now been set to HAS_MOVED. On the next clock tick, the agent's main task will now know that the move has been executed. Sometimes a move will fail; if this occurs the MovingHelloTask will fail as well, since it cannot fulfil the task it had promised to do.
The agent will also need to send a message to announce that it has arrived in a new habitat. Since its main task was only to move the agent, the task calls the succeed() method to exit.
private void reasonAway() {
// Print a message and succeed task
println("Hello Brave New World!");
succeed();
}
Finally, the messages are logged with a small prefix showing the identifier MoveAgent and its location. The location of the agent is accessed through the agent context. The agent context contains the information that is known about the agent. This information is the same for all tasks. This is accessible through the method getAgentContext() in Task.
private void println(String message) {
System.out.println(
"[MoveAgent in "
+ getAgentContext().getMyAddress().getHabitatName()
+ "] "
+ message);
}
The following XML script creates a habitat and launches a moving agent.
<habitat>
<agent dna="agents/move.dna">
</agent>
</habitat>
You can run this example by entering the following command in the examples sub-directory:
habitat ..\examples\move\move.xml
In conventional programming, the MovingHelloTask would usually be programmed using a structure similar to the one below:
reasonAtHome();
startGoingAway()
while (! hasMoved()) {
wait();
}
reasonAway();
This results in a busy-wait loop until the agent has arrived. However, the task model does not allow this style of programming. Moving is initiated by sending a message, which is an asynchronous action. The hasMoved() condition can only be tested in an incoming message handler processing the reply message.
So, how does an agent wait until something occurs? Using information pertaining to the state is a possible way to deal with this problem. In the MovingHelloTask shown above, the TRYING_TO_MOVE state corresponds to a busy-wait loop. Using the heartbeat method handleHeartbeat, the calling of reasonAway is postponed until the state is set to HAS_MOVED, indicating the agent has moved. The state won't be set to HAS_MOVED until the MoveTask has successfully ended. You might be wondering, how does the MoveTask know? Eventually, a task has to process an incoming message, realize that the move has been successful, and succeed().This condition is tested in taskEnded.
Explicitly remembering state information is not the only possible approach. A different approach is to use the task scheduler. This class allows you to add tasks and specify in which order they are executed.
For example, see the version below of the MovingHelloTask, rewritten to extend the class TaskScheduler.:
package tryllian.examples.afc.move2;
import tryllian.afc.task.TaskScheduler;
import tryllian.afc.task.standard.MoveTask;
import tryllian.afc.task.standard.LogTask;
/**
* The functionality of this task is identical to that of
* tryllian.examples.afc.move.MovingHelloTask,
* but instead of keeping our explicit state information,
* we leave task scheduling to TaskScheduler.
*
* @see tryllian.examples.afc.move.MovingHelloTask
*/
public class MovingHelloTask extends TaskScheduler
{
private final String destination;
/**
* Creates a MovingHelloTask and schedules the needed subtasks.
*/
public MovingHelloTask(String destination)
{
this.destination = destination;
// Tasks to be exeucted.
LogTask helloWorld = new LogTask("Hello Agent World!");
LogTask moveAnnouncement = new LogTask("Trying to move to "
+ destination);
MoveTask moveToHello = new MoveTask(destination);
LogTask helloNewWorld = new LogTask("Hello Brave New World!");
// The following defines the order the tasks are executed in.
// This is the same behavior as the original move example.
// AddTask() adds a task to the scheduler and registers which task
// will be executed next.
// Affter saying hello, tell that you are moving.
addTask(helloWorld, moveAnnouncement);
// Move after the move announcement.
addTask(moveAnnouncement, moveToHello);
// Print another message after moving.
addTask(moveToHello, helloNewWorld);
// Finish when the final message has been printed.
// The first 'null' value inidcates that the scheduler should succeed
// if helloNewWorld succeeded; the second null that it should fail in
// case of failure of that task.
addTask(helloNewWorld, null, null); // no task after helloNewWorld
// Start by saying hello.
// setFirstTask is called AFTER adding the tasks to be able to make
// sure the first has been added.
setFirstTask (helloWorld);
}
}
Printing a message has been encapsulated in a task, as well. We use the LogTask to do this, one of the standard tasks provided in the AFC. These tasks and other frequently used tasks can be found in the package tryllian.afc.task.standard.
This example can be run by starting move2.xml from the corresponding examples sub-directory:
habitat ..\examples\move2\move2.xml
The output is the same as the previous move example.
Above it is explained how to create an agent within the Tryllian ADK examples directory. This section describes how to create an agent in a separate directory.
Let's say you are creating your own agent in a directory named myADK. Writing java files, compiling and packaging the class files in a jar file and DNA file works the same as above. After performing these steps, an XML script has be to written (see also previous section). Create the XML file, myagent.xml in the myADK directory. The tag defining the agent (or agents) is:
<agent dna="mypackage.MyFirstAgent">
</agent>
To run the XML file, you need to copy the habitat script file to the myADK directory. Modify the habitat.bat file by setting the value of ADK_ROOT to the directory where the ADK has been installed in (for the examples dir ".." worked fine, now you need to set the absolute pathname, for example, C:\software\adk or /home/user/java/adk). Now, you can copy the habitat file anywhere and it should be able to find the jars of the ADK.
As seen before, the most important properties of an agent are its behavior and its identity. Behavior is implemented in the form of tasks, which are coded in separate source files and plugged into the agent; identity is largely assigned by the ARE. It follows that the agent source file itself is very simple indeed. Source code examples for agents are provided in the chapter on the Task Model (Chapter 7, The Agent Task Model), which shows how to program an agent together with its tasks. For now, we will provide a simple source file that will not compile, but can be used as a template agent source file, which we will call ExampleAgent. Both the source file and its tasks (if specific for this agent) should be together in a package. As usual, we expect the package name to extend your company's website name in reverse.
In ExampleAgent.java source looks like this:
// Package name extending your company website name in reverse
// This package includes the tasks necessary to make the agent behave properly.
package com.yourcompany.exampleagent;
// We import the Agent class we will extend.
import tryllian.afc.agent.Agent;
// The class itself.
public class ExampleAgent extends Agent {
// Declare tasks for the agent's main activities.
// We imagine there to be one task, appropriately named ExampleTask.
private ExampleTask exampleTask;
public ExampleAgent()
{
// The constructor only uses the setName() method.
// Other initializations go in the agentStarted() method.
setName("AnExampleAgent");
}
// Initialization code: create a new task and add it to the agent.
// This will get the agent going.
public void agentStarted() {
addTask(new ExampleTask());
}
}
The Agent API is further explained in detail in the Task Model chapter (Chapter 7, The Agent Task Model).
The initial lay-out of a Habitat is described by an XML file. This file describes the the agents that should be created on startup.
The full DTD is given below. Its official definition can be found at http://www.tryllian.com/xml/habitat_1_1.dtd.
<!-- Every file is contained in the 'habitat' element -->
<!ELEMENT habitat ((on-exception)?, (agent)*)>
<!ELEMENT on-exception EMPTY>
<!ATTLIST on-exception
action (stop | silent | log) "log"
>
<!-- The agent element defines an agent in the habitat -->
<!ELEMENT agent (arg)*>
<!ATTLIST agent
nickname CDATA ""
dna CDATA #REQUIRED
habitat CDATA #IMPLIED
>
<!-- An agent argument. This is passed to the agent immediately -->
<!ELEMENT arg EMPTY>
<!ATTLIST arg
value CDATA #REQUIRED
>
Example 5.1. An example Habitat XML file
<!DOCTYPE habitat "http://www.tryllian.com/xml/habitat_1_0.dtd">
<habitat>
<on-exception action="stop"/>
<agent dna="httpservice.dna">
<arg value="8080"/>
</agent>
<agent dna="soapservice.dna"/>
</habitat>
The on-exception tag describes what to do when the habitat could not be started. The values for the parameter action have the following meaning:
Log the error to logoutput.xml and try to continue.
Do not mention the error and try to continue.
Show the error on screen and stop the entire process.
The tag agent describes an agent that should be added. Every agent tag adds exactly one agent. The attributes have the following meaning:
The location of the DNA file. Relative files are resolved against the classloader.basedir property. Absolute files are allowed, but only if they lie within the path specified in the classloader.basedir property.
The on-exception tag replaces the old onException tag.
A special type of agent is the System Agent or SA. A System Agent is different from an ordinary agent in three ways:
It is owned by the habitat itself, rather than by any end user.
It never leaves the habitat it is in.
It is guaranteed to be present in a habitat.
System agents exist to provide certain services for your agents. They exist because agents should never interact directly with the system on which they reside.
Here is a list of the various SAs that exist. Communicating with SAs is mostly done through predefined tasks. The chapter on agent communication (Chapter 9, Behind Agent Communication) provides more details on how to communicate directly with each SA.
Information on additional SAs can be found in the ARE Technical Specifications document. You will usually not interact with these agents directly. Most functionality has been encapsulated in ready-made tasks, which can be easily plugged into an agent.
The DNA Composer is a tool to generate agent DNA files. You can use this tool instead of the ANT task provided in the build structure of the agent template project. Another way to create DNA files is to use the Deployment tool (Section 5.9, “Agent Deployment Tool”)which is described later in this chapter and allows you to quickly generate agent DNA. However, to fine tune the DNA files, especially in complex applications, it is better to use the DNA Composer.
A DNA file is a special kind of jar file that contains a descriptor file. It specifies the agent class, contained jarfiles, and shared references/jarfiles (which are stored somewhere else, but on which the agent classes depend). By using the DNA Composer you can sign your jar files, and put them in the DNA file together with a descriptor file.
dnacomposer
--create, -c
[
--verbose, -v
] [
--nocompression, -n
]
--file, -f
dnafile.dna
--descriptor, -d
descriptor.xml
[
--jarpath, -j
directory
]
--keystore, -k
keystore.dat
--alias, -a
alias
[
--password, -p
password
]
dnacomposer
--update, -u
original.dna
[
--verbose, -v
] [
--nocompression, -n
] [
--file, -f
dnafile.dna
] [
--descriptor, -d
descriptor.xml
]
--keystore, -k
keystore.dat
--alias, -a
alias
[
--password, -p
password
]
file.jar
...
The dnacomposer command creates a new DNA file that contains the classes that define an agent in zero or more jarfiles, and an agent description file in XML format.
This section describes how the parameters of the command-line tool are used in the process of creating DNA files. More information can be found in the ARE specifications.
When the command is run with the --create option, all the jarfiles specified present in the jarpath directory are stored in the DNA file specified by the --file.
The jarfiles are signed on storing using the private key retrieved from the keystore using the alias and password. When the descriptor and all the jarfiles have been added, the entire DNA file will be signed as well.
When the command is run with the --update option, only those jarfiles specified on the commandline are stored in the DNA file specified with the --file option; jarfiles that were already in the DNA file and were not specified on the commandline, are copied from the original DNA file and are not signed again.
When a new agent descriptor has been specified using the -d option, jarfiles that are no longer required are removed from the DNA file.
When at least one jarfile has been added, removed or replaced, or the agent descriptor has changed, the entire DNA file will be signed again. In other words, if nothing changes, an identical DNA file will be created.
This option indicates that a new DNA file will be created. The name of the new DNA file must be specified using the --file option.
This option indicates that an existing DNA file will be updated. The parameter of this argument indicates the DNA file that will be updated. If the --file option is not given, the original DNA file will be overwritten.
This option also requires a list of those jarfiles that will be replaced or added to the new DNA file.
Causes logging of output (in the logfile) for debugging purposes.
Suppresses the default ZIP compression when writing the DNA file.
This option specifies the name of the output DNA file. This option is not required when a DNA file is upgraded.
This option specifies the filename of the agent descriptor XML file. If the --update option has been given, the agent descriptor is optional. If the --create option has been given, this parameter is required.
This option specifies the directory where the jarfiles specified in the agent descriptor can be found. If this option is omitted, the current working directory will be used. If this option is used, the current working directory will not be used.
This option takes only one directory as argument.
This option is followed by the filename of the keystore; this keystore is used to retrieve the private key with which the DNA File and its contents will be signed.
The alias specifies which private key is to be used.
Private keys are usually protected by a password. This password can be specified on the command line by using this option. It is also possible to omit this password; in this case, the dnacomposer will obtain the password interactively.
A list of jarfiles. When updating a DNA file, this is the list of the new jarfiles.
When a DNA file is created, all the <file>s in the agent descriptor must be present in the jarpath directory. If one of these files can not be found, an error will be given and the DNA file will not be created.
When a DNA file is updated, all the jarfiles specified on the command line must be specified in the agent descriptor. If one of these files is not specified, an error will be given the DNA file will not be created or overwritten.
When a DNA file is updated and one of the jarfiles specified in the agent descriptor is not present in the original and not specified on the command line, an error will be given and the DNA file will not be created or overwritten.
When any I/O error occurs, the error will be displayed and the DNA file will not be created or overwritten.
This example is based on the following agent descriptor.
<!-- A descriptor for the DNA file of the Hello World agent. -->
<agentdescriptor>
<target target-id="standard">
<environment>
<java areversions="1.3"
agentclass="tryllian.scenarios.helloworld.HelloWorldAgent"
/>
</environment>
<file name="helloworld.jar"/>
<shared-ref type="stdlib" name="afc"/>
</target>
</agentdescriptor>
Given that the helloworld.jar file resides in the directory c:\mydir, a new DNA file can be created using the following command.
dnacomposer --create --file helloworld.dna
--jarpath c:\mydir -d mydescriptor.xml
--keystore c:\adk-2.1.1\config\keystore.dat --alias developer
We can now extend the descriptor to the following descriptor:
<!-- A descriptor for the DNA file of the Hello World agent. -->
<agentdescriptor>
<target target-id="standard">
<environment>
<java areversions="1.3"
agentclass="tryllian.scenarios.helloworld.HelloWorldAgent"
/>
</environment>
<file name="helloworld.jar"/>
<file name="specialcode.jar"/>
<shared-ref type="stdlib" name="afc"/>
</target>
</agentdescriptor>
If the file specialcode.jar resides in the current directory, we can update the old DNA file as follows:
dnacomposer --update helloworld.dna
--keystore c:\adk-2.1.1\config\keystore.dat --alias developer specialcode.jar
This will overwrite the old helloworld.dna.
Using ANT to compile your agents, also allows you to create your agent with ANT. A special target exists for ANT that composes your agent. The DNA task for ANT allows you to create properly signed agents from any ANT build script. (See ANT for more information about ANT in general.)
The library which contains the ANT tasks specific to Tryllian's ADK are included in the <ADK_ROOT>\ant\lib\trylliant.jar file.
Next, tell ANT it has a new target available. You can do this by adding the following to your ANT script (build.xml):
<taskdef name="dna"
classname="com.tryllian.adk.tools.trylliant.DNAAntTask"/>
This is already done in the build.xml files included in the ADK.
The DNA target for ANT creates the dna file for your agent. You can use it like any other ANT target. It takes an element named jarfileset, which behaves like a regular ANT fileset. Furthermore, it has various attributes. These attributes match their equivalent argument for the command-line dna-composer (see Section 5.7, “DNA Composer”) for creating an agent:
DNA target attributes
The name of the DNA file that is to be created. This attribute is required.
The descriptor file for the agent. This attribute is required.
The alias in the keystore to sign the dna with. This attribute is required.
The keystore file that holds the key to sign the dna with. This attribute is required.
The password of the keystore. This attribute is required.
Suppresses the default ZIP compression when writing the DNA file. This attribute is optional, and defaults to false, set it to true to disable compression.
Example 5.2. DNA ANT usage
<dna file="agents/myagent.dna"
descriptor="descriptors/myagent.xml"
keystore="config/keystore.dat"
alias="developer1"
password="developer1">
<jarfileset dir="jars"
includes="myagent*.jar" />
</dna>
You can run ANT in verbose mode for more detailed information about the created dna. You normally start ANT in verbose mode by adding a -v parameter to the command-line of ANT.
The easiest way to convert your Agent classes into a live Agent is to use the Agent Deployment Tool. This tool can be used to specify the libraries and resources, security settings and deployment parameters. The tool consists of two wizards: one for construction of the DNA and one for the actual deployment.
Before you can use this tool, it is assumed that you have compiled your Agents and have a collection of class files ready. If your Agent imports any classes in third-party jars, you will need those as well. That's all. Packaging the Agent in a jar file, creating the agent descriptor and creating the actual DNA using the DNA Composer is all done by this tool.
Start the Agent Deployment Tool by executing deploytool in the <ADK_ROOT>. It will create a deploycm directory so the JXTA directory of your habitat and the one of the Agent Deployment Tool won't overlap with each other. The default settings are used to communicate with your habitat. If you want to create an agent with a special habitat, you need to create in the <ADK_ROOT>\config directory a deploytool.properties with the properties of your habitat like another port number, another keystore or enable the secure pipe. Use the properties as set in <ADK_ROOT>\config\habitat.properties.
After startup, the first step is to create a new Agent project. Such a project enables you to construct and deploy separate Agents independently. You can switch between the Agents in your project and construct and deploy them separately. To create a new Agent project, select "New" from the "Project" menu. Specify the name and location of the project file in the file chooser.
Having set up a new project, you can now add your Agent(s). Select "Add" from the Agent menu. A new entry named "new agent 1" is added to the "Agent" menu to indicate the new, nameless Agent. Adding more agents will result in this list to grow. Switching between Agents is done by selecting the Agent from the list.
Using the construction wizard, first specify the class files for this agent. Click "Add" (at bottom of the window) and select the class file using the file chooser. As an added convenience, you can also select a directory: all class files in this directory (and its subdirectories) are added to the list. You will see that the application automatically detects the package information of the classes and whether or not a class file contains an Agent. If so, a checkbox in the first column is checked. If there are more agent class files selected, you can change the agent main class by checking an other checkbox. If an agent class is found, the "Next" button of the construction wizard will be enabled.
The second step enables you to list all jars (and zips) the Agent needs. Click "Add" and select all files. Again, selecting a directory adds all jars in any subdirectory to the list. The first column indicates if the library is shared: shared libraries are not included in the DNA file. Instead, you must add a reference to the library in your dna.catalog which can be found in the config directory of your ADK installation. Click "DNA catalog" to see the entries that must be included in the dna.catalog. Since libraries are optional, you can skip this step in the wizard.
Next, you can specify the resources for your Agent. Resources are no jars or class files but could be text, HTML or images. Use "Add" to select all resources or directories containing resources. Your Agent and its Tasks can access these resources using the getResource(String) or getResourceAsStream(String) method in java.lang.Class. Make sure to start the filename with a forward slash:
URL resource = getClass().getResource("/somefile.txt");Resources are optional, so you can skip this step in the wizard, if desired..
The final construction step consists of security settings for your Agent. You must specify a keystore file, an alias and a corresponding password. Keystores are located in the config directory of the ADK distribution, or you can make them yourself using the Keytool (see section Section 12.9, “The Key Tool”).
If you check "Save password", the password will be saved as plain text in the project file, which is generated when you select "Save" from the "Project" menu. Depending on your project's security level, you may want to uncheck this option. Then, the password is not saved and you will have to enter it each time after restart of the deployment tool.
Once you have specified a keystore and an alias, the "Finish" button will be enabled. Click it to start the DNA construction process. If something goes wrong (a file cannot be found for example), you will be notified and the process will be stopped. If all goes well, the agent will be created and its DNA file will be placed in the current deployment tool work directory. This is found in the bin/deploytoolcm directory of the ADK installation. DNA creation will take longer if you have specified any (non-shared) libraries.
Now you have a DNA file for your Agent, you can deploy it in a habitat. The Quickstart Guide explains for to setup and run a habitat. Assuming you have an up-and-running habitat, you can switch to the "Deployment" tab.
The current deployment tool only supports local deployment. You can only deploy your Agents in a habitat running on the same machine. If you want remote deployment, the easiest solution is to execute a MoveTask on startup of your Agent.
The deployment wizard consist of a single step where you must specify the DNA file, the habitat and any constructor arguments for your Agent.
DNA file: The DNA file for your Agent. If you just completed the construction wizard, a filename will be shown in the box. Otherwise, you must use the "Browse" button and select a DNA file.
Habitat host: The host that is running a habitat. Since this must be a local habitat, the local host name is used as a default.
Habitat port: The port used by the habitat. 30434 is the default value. The port of the habitat is specified in the config/habitat.properties file.
Constructor arguments: Any arguments you want to pass on to your Agent on startup. For example, to get the host to move to initially, the number of retries for a particular task, etc. Make sure to implement a constructor that takes a String[] as an argument, otherwise deployment will fail. If you do not specify arguments, this is not necessary.
If you specify a DNA file and a port number, the "Finish" button of the deployment wizard will be enabled. Clicking it will initiate the deployment process: the habitat is contacted and the Agent is created. It is possible that deployment takes longer (around 30 seconds) the first time, or might even timeout. Make sure the correct habitat is contacted and it is running and retry deploying it by clicking the "Finish" button again. If deployment keeps failing check the port number.
[3] ANT is a Java-based build tool. It is included in the ADK distribution for more info see the Ant website: http://jakarta.apache.org/ant.