Agent Itinraries
by Russell P. Lentini, Goutham P. Rao, Jon N. Thies 


Listing One
// FILE State.java 
package finitestateitinerary;
/** 
 *  This class is an encapsulation of the program that must be run by 
 *  the FS Itinerary when it is in a particular configuration.
 *  The entry point is the run method. 
 */
public abstract class State implements java.io.Serializable {
    /* INIT state is the initial state representation of the finite 
     * state machine. */
    public static final State INIT = new State() {
        public void run() {
        }
    };
    /* HALT state is the final state representation of the finite 
     * state machine */
    public static final State HALT = new State() {
        public void run() throws Halt {
            throw new Halt("HALT : Halted because of normal termination");
        }
    };
     
    /* Applications must implement this method for functionality */ 
    public abstract void run() throws Halt;
}

Listing Two
// FILE TransitionFunction.java
package finitestateitinerary;
/**
 *  The application must implement the TransitionFunction class to return 
 *  the next configuration.
 */
public interface TransitionFunction {
    public Configuration nextConfiguration() throws Halt;
}

Listing Three
// FILE Configuration.java
package finitestateitinerary;
/**
 *  The configuration class holds the state to be executed and transitions 
 *  from this configuration to new ones based on real-time conditions.  The 
 *  next configuration is chosen by the implementation of the 
 *  TransitionFunction
 */
public abstract class Configuration extends State 
                                    implements TransitionFunction {
     
}

Listing Four
// FILE FiniteStateItinerary.java
package finitestateitinerary;
import java.io.*;
/**
 *  The run method of this class actually starts running the meta-program. 
 *  It begins with the initial configuration, calls it's nextConfiguration 
 *  method.  The nextConfiguration method evaluates real time conditions and
 *  and returns the next configuration.  The initial state is a spurious state 
 *  and is never executed by the itinerary.
 */ 
public class FiniteStateItinerary implements Serializable {
    private Configuration configuration = null;
    public FiniteStateItinerary(Configuration configuration) {
        this.configuration = configuration;
    }
    public void run() {
        try {
            /* Loop while the current  condition specifies that the itinerary 
             * should continue evaluation. */
            while (true) {
                /* Get the next configuration and hence the corresponding
                 * state to move into. */
                configuration = configuration.nextConfiguration(); 
                if (configuration == null) {
                    return;
                }
                /* Run the program associated with the state object. */ 
                configuration.run();
            }
        } catch (Halt halt) {
        }
        /* At this point, either final condition or an unsatisfyable 
         * condition has been encountered, or the machine was halted. */
    }
}

Listing Five
// FILE Halt.java 
package finitestateitinerary;
public class Halt extends Throwable { 
}

Listing Six
// FILE MobileCondition.java
package finitestateagent;
import java.io.*;
import java.net.*;
import finitestateitinerary.*;
/**
 *  This implementation of the Condition object provides the mobile nature
 *  to agent applications.  It causes the Finite State Itinerary to be written 
 *  to a computer.  It relies on the CommunicationLink class to accomplish this 
 *  via object serialization.  If the evaluate() method returns true, the
 *  transition fucntion will choose the corresponding configuration as the
 *  next configuration.  If this configuration conatins a migrate state, the 
 *  FS itinerary will migrate to the destination machine.
 */
public class MobileCondition extends Condition implements Serializable {
    private InetAddress destination = null;
    public MobileCondition(InetAddress destination) {
        this.destination = destination;
    }
    public boolean evaluate() {
        try {
            if (InetAddress.getLocalHost().equals(destination)) {
                return false;
            }
            else {
                return true;
            }
        } catch (UnknownHostException e) {
            throw new Halt();
            /* This computer is not suitable for IP communications */
        }
    }
}

Listing Seven
// FILE MigrateTask.java
package finitestateagent;
import java.net.*;
import finitestateitinerary.*;
/**
 *  This state causes the entire finite state itinerary to be sent to the 
 *  destination machine via the CommunicationLink, where the finite state 
 *  itinerary will resume execution.  This state then halts the execution of 
 *  the itinerary on the current machine by halting the current thread, since 
 *  a copy of the FS itinerary is now running at the destination machine.
 */
public class MigrateState extends State {
    public MigrateState(InetAddress destination, FiniteStateItinerary 
                        itinerary)
        this.destination = destination;
        this.itinerary = itinerary;
    }
    /**
     *  The run method is implemented to migrate the agent 
     *  that owns this state, to a new machine
     */
    public void run() throws Halt {
        /* Migrate this finite state itinerary to the destination machine */
     
        /* Stop the local execution of this instance of finitestateitinerary
         * since a copy is now running on a new machine */
        throw new Halt("HALT : Because itinerary transfered to destination" );
    }
}

Listing Eight
// FILE FSIConfiguration.java 
package finitestateagent;
import java.net.*;
import java.io.*;
import java.util.*;
import finitestateitinerary.*;
/**
 *  A configuration with a default deterministic transition function with 
 *  mobility support
 */
public class FSIConfiguration extends Configuration 
                              implements Serializable {
    private State state = null;
    protected Vector transitions = new Vector(); 
    public FSIConfiguration(State state) {
        this.state = state;
    }
    /**
     *  Add a condition and a target configuration branch on the finite 
     *  state machine
     */
    public void addTransition(Condition condition, 
                              Configuration configuration) {
        transitions.addElement(new Transition(condition, configuration));
    }
    /**
     *  Retreive the first met configuration to move into 
     */
    public Configuration nextConfiguration() throws Halt {
        for (int i = 0; i < transitions.size(); i++) {
            if (((Transition)transitions.elementAt(i)).condition.evaluate()) {
                return ((Transition)transitions.elementAt(i)).configuration;
            }
        }
        throw new Halt("HALT : No satisfiable condition");
    }
    /**
     *  Execute the state
     */
    public void run() throws Halt {
        state.run();
    }
    /**
     *  Represents one transition mapping 
     */
    private class Transition implements Serializable {
        private Condition condition = null;
        private Configuration configuration = null;
        public Transition(Condition condition, Configuration configuration) {
            this.condition = condition;
            this.configuration = configuration;
        }
    }
}

Listing Nine
// FILE Condition.java
package finitestateagent;
import java.io.*;
import com.lmco.atl.finitestateitinerary.*;
public abstract class Condition implements Serializable {
    /* This static class always causes a transition to the associated 
     * configuration */
    public static final Condition DEFAULT_CONDITION = new Condition() {
        public boolean evaluate() {
            return true;
        }
    };
    public abstract boolean evaluate() throws Halt;
}

Listing Ten
import finitestateitinerary.*;
import finitestateagent.*;
import java.net.InetAddress;
     
public class DemoAgent {
    public FiniteStateItinerary createMetaProgram(InetAddress eniac) {
        // Create the root configuration
        FSIConfiguration root = new FSIConfiguration(State.INIT);
    // Create the itinerary
    FiniteStateItinerary fsi = new FiniteStateItinerary(root); 
    // Now create all other configurations
        // NOTE : jdbc.JDBCQueryTask would be implemented to simply 
        // perform the query passed on some database.
    jdbc.JDBCQueryTask jdbcTask1 = new jdbc.JDBCQueryTask(
       "SELECT Something FROM Somewhere");
    jdbc.JDBCQueryTask jdbcTask2 = new jdbc.JDBCQueryTask(
       "SELECT SomethingElse FROM SomewhereElse");
        FSIConfiguration dbConfig1 = new FSIConfiguration(jdbcTask1); 
        FSIConfiguration dbConfig2 = new FSIConfiguration(jdbcTask2); 
        FSIConfiguration migrateConfig = new FSIConfiguration(
        new MigrateState(eniac, fsi));
        FSIConfiguration haltConfig = new FSIConfiguration(State.HALT); 
        // Link the above configurations using conditions
        // NOTE : In our implementation of 
    // TransitionFunction.nextConfiguration, we traverse a Vector to 
    // test conditions.  This means that the conditions will be 
    // tested in the order they are added.
        root.addTransition(new MobileCondition(eniac), migrateConfig); 
        root.addTransition(Condition.DEFAULT_CONDITION, dbConfig1); 
        migrateConfig.addTransition(Condition.DEFAULT_CONDITION, dbConfig1); 
        // NOTE : DBCondition tests for the number of records 
        // retreived by a query.  In this example, if the number of records 
    // is less than 100, the condition returns true.  Otherwise, 
    // the 2nd transition will be taken.
        dbConfig1.addTransition(new DBCondition(jdbcTask1, 100), dbConfig2); 
        dbConfig1.addTransition(Condition.DEFAULT_CONDITION, haltConfig); 
        dbConfig2.addTransition(Condition.DEFAULT_CONDITION, haltConfig);
        // Return the transition graph
        return fsi;
    }   
}
     





