/* * OAAAgent.java * * * Copyright (c) 2004 David Hjelm . * * * Project homepage: . * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ package se.gu.ling.trindikit.oaa.common; import com.sri.oaa2.com.*; import com.sri.oaa2.icl.*; import com.sri.oaa2.lib.*; import java.util.*; /**

OAAAgent is an abstract base class for creating OAA agents written in java. It handles callback, registration and declaration of solvables to the OAA facilitator. More information about OAA can be found here. OAA version 2.2 or later is needed

To extend this class to create an OAA agent:

When the agent receives an incoming oaa_Solve event, it iterates through all solvers defined for the current state. If the solvable of a solver unifies with the query the solve method of the solver is executed, and all resulting answers are added to the list of potential answers. If the pre-state is the same as the post-state and no exceptions have been thrown the agent will continue to iterate until all the solvers of the current state have been examined.

A solver can be associated to several pre-states but can have only one explicit post-state. If no post-state is specified when the solver is added the agent does not change its state. It is however possible (but not advisable) to add the same solver twice, with different post-states. In that case solver added first will attempt to solve an incoming query first. It is also possible to assign the same solvable to two different solvers. Also here, the solver added first has precedence.

Exceptions thrown in solver classes can be propagated to the agent class by throwing a SolveException which encapsulates the exception. By overriding handleSolveException exceptions can be dealt with in an application-specific way. The default behaviour of handleSolveException is to reset the agent to its start state.

The agent can also make outgoing oaaSolve calls using the solve method. To make sure that outgoing oaaSolve calls are not blocked by incoming oaaSolve calls, solve uses a OAA library of its own. This way the agent can propagate e.g. events originating from another thread to the OAA community. It is probably not the best solution, but it works...

@author David Hjelm @version February 2003 */ public class OAAAgent { //some icl constants final static IclList EMPTY_LIST = new IclList(); final static IclList DO_ALL = (IclList) IclUtils.fromString(false, "[do_all(true)]"); final static IclList OVERWRITE = (IclList) IclUtils.fromString(false, "[if_exists(overwrite)]"); //current state of the agent private int state; //start state of the agent private int start_state; //an array with the OAAStateSolver vectors, //an OAAStateSolver contains an OAASolver and a post state private Set[] handlers; //OAA library. LibOaa has one single thread, so we need to have //one LibOaa for incoming and another for outgoing solve events. private LibOaa inOaa; //private LibOaa outOaa; /** This must be called from the constructor of the implementing class. @param initState the start state of the agent. */ public OAAAgent(int initState, int no_of_states) { if(no_of_states < 1){ System.err.println("Error in OAA Agent constructor"); System.err.println("OAA Agent must have at least one state"); System.err.println(""+no_of_states+" specified"); System.exit(0); } if(initState<0 || initState > no_of_states-1 ){ System.err.println("Error in OAA Agent constructor"); System.err.println("Initial state must be in the range 0 ... "+ (no_of_states-1)); System.exit(0); } handlers = new HashSet[no_of_states]; for(int i=0;isolver, makes it callable in state and makes the agent enter postState after solver has successfully solved a query. E.g.: addSolver(1,2,solver); makes solver callable in state 1 and makes the agent enter state 2 after solver has successfully solved a query. @param state the agent state in which solver is callable @param postState the agent state when solver has successfully solved a query @param solver the OAASolver instance */ public void addSolver(int state, OAASolver solver) { if(state < 0 || state > handlers.length-1){ System.err.println("Trying to add solver "+solver+ " for non-existing state "+state); System.err.println("Solvers can only be added to states 0 ... "+ (handlers.length-1)); System.exit(0); } handlers[state].add(solver); } /** Adds the OAASolver solver, makes it callable in any of agent states states and makes the agent change state to the state that has the same index in postStates as the state it is called in has in states. E.g.: addSolver(new int[]{1,2},new int[]{2,1},solver); makes solver callable in states 1 and 2. If it is called in state 1 the agent changes state to 2 and if it is called in state 2 the agent changes state to 1. @param states the agent states in which solver is callable @param postStates the agent states when the solver has successfully solved a query @param solver the OAASolver instance */ public void addSolver(int[] states, OAASolver solver) { for (int i = 0; i < states.length; ++i) addSolver(states[i], solver); } /** Connects to facilitator at host:port using name agentName and declares the set of solvables declared for the current state (i.e. the start state). @param host the host name of the machine on which the facilitator is running @param port the port that the facilitator is listening to @param name the name of the agent in the OAA community */ protected boolean register(String agentName, String[] libcomargs) { //setup oaa library for incoming events //we want to open a port for the direct_connect facility /* wait with this {"-oaa_connect","tcp('"+host+"',"+port+")", "-oaa_listen","tcp(localhost,9999)"}; */ //connect to facilitator and register inOaa = new LibOaa(new LibCom(new LibComTcpProtocol(), libcomargs)); if (!inOaa.oaaSetupCommunication(agentName)) { System.err.println("Couldnt connect to facilitator"); return false; } if (!inOaa.oaaRegister("parent", agentName, new IclList(), new IclList())) { System.err.println("Could not register"); return false; } //register callback inOaa.oaaRegisterCallback("oaa_AppDoEvent", new OAAEventListener() { public boolean doOAAEvent(IclTerm goal, IclList params, IclList answers) { return handleEvent(goal, params, answers); } }); //setup oaa library for outgoing events //This does not work, deadlock for outOaa, investigate other solution /* outOaa = new LibOaa(new LibCom(new LibComTcpProtocol(),libcomargs)); if (!outOaa.oaaSetupCommunication("dummy")){ System.err.println("Couldnt connect to facilitator"); return false; } if (!outOaa.oaaRegister("parent", "dummy", new IclList(), new IclList())) { System.err.println("Could not register"); return false; } //declare current state's solvables outOaa.oaaReady(true);*/ //say "I'm ready for action!!!" inOaa.oaaReady(true); declareSolvables(); return true; } /** Disconnects from facilitator */ public boolean disconnect() { inOaa.oaaDisconnect(EMPTY_LIST); //outOaa.oaaDisconnect(EMPTY_LIST); return true; } /** Takes care of incoming OAA events, e.g. oaa_Solve calls, delegates them to the appropriate OAASolvers for the current state and returns a (possibly empty) set of answers. @param goal The OAA query. Should match one of the solvables of the current state. @param params List of additional parameters passed with the query @param answers Any answers to query from the agent. handleEvent does not have to be called explicitly. It is registered as a callback function as part of the register method. */ //no longer synchronized public boolean handleEvent(IclTerm goal, IclList pars, IclList answers){ System.out.println("OAAAgent: "+ goal+" called"); boolean solved = false; Unifier u = Unifier.getInstance(); //global variable handlers = array of sets of solvers //global variable state = current state that the agent is in //so set of solvers for current state is at handlers[state] for(Iterator i = handlers[state].iterator();i.hasNext();){ OAASolver solver = (OAASolver)i.next(); if( u.unify(goal, solver.getSolvable())!=null && //this will add solver's answers to answers solver.solve(goal,pars,answers) ){ solved = true; } } return solved; } /** Makes outgoing oaaSolve calls. Just calls oaaSolve in LibOaa. @param goal The outgoing query @param params Additional parameters to be passed with the query. @param answers Answers to the query. */ public boolean solve(IclTerm goal, IclList params, IclList answers) { boolean solved = inOaa.oaaSolve(goal, params, answers); return solved; } /** If newstate exists sets state to newstate and redeclares solvables. @param newstate the state to switch to */ public void setState(int newState) { if (newState > -1 && newState < handlers.length) { state = newState; declareSolvables(); } else { System.err.println("Attempt to change to a non/existing state"); System.err.println("State not changed"); } } /** returns the current state */ public int getState() { return state; } /** Declares the set of solvables associated with the current state */ public void declareSolvables() { IclList solvables = new IclList(); Iterator it = handlers[state].iterator(); while (it.hasNext()) { solvables.add(((OAASolver)it.next()).getSolvable()); } System.err.println("Try to declare: " + solvables); IclTerm declared = inOaa.oaaDeclare(solvables, EMPTY_LIST, EMPTY_LIST, OVERWRITE); } /** * Method to get the address of a known agent. * @param agentName - the name of the agent * @return the agent address */ public String getAgentAddress(String agentName) { IclList res = new IclList(); inOaa.oaaSolve(IclTerm.fromString(true, "agent_host(Addr,'" + agentName + "',Host)"), new IclList(), res); if (res.size() == 0) { return ""; } String agentAddr = res.getTerm(0).getTerm(0).toString(); return agentAddr; } }