📄 liboaa.java
字号:
/*
* @(#)LibOaa.java 08/1999
*
* The contents of this file are subject to the OAA Community Research
* License Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License
* at http://www.ai.sri.com/~oaa/. Software distributed under the License
* is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the License for the specific language governing
* rights and limitations under the License. Portions of the software are
* Copyright (c) SRI International, 1999. All rights reserved.
* "OAA" is a registered trademark, and "Open Agent Architecture" is a
* trademark, of SRI International, a California nonprofit public benefit
* corporation.
*
*/
package com.sri.oaa2.lib;
import java.io.*;
import java.util.*;
import java.lang.String;
import java.net.*;
import com.sri.oaa2.com.*;
import com.sri.oaa2.icl.*;
import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue;
import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.apache.log4j.xml.DOMConfigurator;
//DEBUG
//import com.sri.oaa2.guiutils.CChronometer;
/**
* This class provides all the facilities to make an application become
* an agent in an Open Agent Architecture community.
*
* <h3>Usage</h3>
* <h4>Creating the OAA library</h4>
* <p>
* To create an agent, create a new instance of the OAA library, passing some
* communication protocol object to use as the transport layer:
*
* <pre>
* LibOaa myOaa = new LibOaa(new LibCom(new LibComTcpProtocol()));
* </pre>
*
* Then, register any callbacks for agent events. Usually, you must
* at least define the callback <code>app_do_event</code> type to handle incoming
* <code>oaaSolve()</code> requests.
* In this example, we link the callback to a function <code>myOAADoEvent</code>,
* which we must later define to handle all capabilities our agent registers with the
* Facilitator.
* (see <code>oaaRegister()</code> below).
*
* <pre>
* myOaa.oaaRegisterCallback("app_do_event",
* new OAAEventListener() {
* public boolean doOAAEvent(IclTerm goal, IclList params, IclList answers) {
* return myOAADoEvent(goal, params, answers);
* }
* });
* </pre>
*
* <h4>Connecting the agent to the facilitator</h4>
* Early on in an agent's lifecycle, it must connect to a Facilitator agent who will
* manage it's communication with other agents. The agent must call the
* method <code>oaaSetupCommunication</code>.
* The name of the agent is supplied as the only argument to this method as follows:
* <pre>
* // First, connects to the facilitator
* if (!myOaa.oaaSetupCommunication("myAgent")) {
* printError("Could not setup");
* return;
* }
* </pre>
* There are three ways to specify the facilitator address, listed here in
* order of preference:
* <ul>
* <l1>
* 1. Specify the default facilitator in the "setup.pl" file. This file
* is located in the home directory on unix and the root c:\ drive on windows.
* The default facilitator is specified using the "default_facilitator" struct.
* For example, the following entry specifies that the faciitator is located
* on "myhost.ai.sri.com" listening on port 4020:
* <pre><code>
* %%%% SETUP FILE FOR OAA %%%%%%%
* default_facilitator(tcp('myhost.ai.sri.com',4020)).
* </code></pre>
* </li>
* <li>
* 2. The "oaa_connect" command line parameter may be used. For example,
* <code>" -oaa_connect "tcp('myhost.ai.sri.com',4020)"</code>
* </li>
* <li>
* 3. The "OAA_CONNECT" System property may be set to the facilitator address.
* The syntax is the same as the "-oaa_connect" command line parameter.
* </li>
* </ul>
* "setup.pl" is always checked first, followed by the command line and then
* the system property.
* <p>
*
* <h4>Enabling direct_connect</h4>
* <p>
* When a client agent sends a request to the facilitator, the facilitator forwards
* the request to one or more agents, receives the responses and then forwards
* the responses back to the calling agent. This means any data passed in
* the communications must be passed through the facilitator. A better solution
* is to pass the data directly from the calling agent to an agent which can
* solve the request.
* </p>
* <p>
* To allow this optimization it is possible to specify the parameter
* "direct_connect(true)" when calling oaaSolve. In order for direct_connect
* to be possible, the following conditions must hold:
* <ul>
* <li>
* 1. There must be only one agent which can solve the request. direct_connect
* does not currently support mulitple solves from multiple agents.
* </li>
* <li>
* 2. The agent which has registered the solvable must be enabled for direct_connect.
* As described below, the "oaa_listen" command line variable is specified
* to enable an agent for direct_connect.
* </li>
* </ul>
* </p>
* <p>
* If these conditions hold and the "direct_connect(true)" parameter is
* specified, oaaSolve will attempt to make a direct connection to the
* appropriate agent in order to solve the request. Once a connection has been
* made it is cached for later use. The connection is terminated when the
* call to <code>oaaDisconnect(IclList)</code> is made.
* </p>
* <p>
* Note that "oaaSetupCommunication" sets up an agent listener for direct_connect.
* It checks the command line variable "oaa_listen" to retrieve the address
* to listen for direct connections. For example, if the command line
* <code>' -oaa_listen "tcp('localhost',4012)" '</code> is supplied, the
* OAA library will open a listen socket on port 4012 used to accept direct
* connections. If the "oaa_listen" command line parameter is not found,
* then the System property "OAA_LISTEN" is checked.
* </p>
* <p>
* Also supported is a binary protocol for efficient data transfer. In the
* current release, this requires another server port for a direct_connect
* enabled agent. The binary server port defaults to the direct_connect
* port with ten added to it. So, in the above example the agent will listen
* on ports 4012 and 4022. The need for a second listen port is temporary and may be
* removed in a future release of OAA. To set the second listen port manually,
* the command line variable "oaa_listen_binary" or system property
* "OAA_LISTEN_BINARY" may be used.
* </p>
* <p>
* In addition to the <code>direct_connect</code> parameter, an <code>address</code>
* parameter may also be specified when calling oaaSolve. Specifying the
* <code>direct_connect</code> and <code>address</code> parameters provides
* the fastest performance OAA can give. The <code>address</code> parameter
* can be retrieved by invoking the <code>agent_host</code> solvable with
* the agent's name. For example, the following code retrieves the agent
* address for an agent by name, and then uses the address and direct_connect
* parameter to achieve maximum performance:
* <pre><code>
* public String getAgentAddress(LibOaa myOaa, String agentName) throws Exception {
* IclList res = new IclList();
*
* myOaa.oaaSolve(IclTerm.fromString(true,
* "agent_host(Addr,'" + agentName + "',Host)"), new IclList(), res);
*
* if (res.size() == 0) {
* throw new Exception("Unable to retrieve agent address using agent_host");
* }
* String agentAddr = res.getTerm(0).getTerm(0).toString();
* return agentAddr;
* }
*
* private IclList solveParams = null;
* public IclList callOaaMaximumPerformance(LibOaa myOaa, IclTerm goal) throws Exception {
* if (solveParams == null) {
* // Retrieves the name of the agent. Note: in order to use
* // this method the name of the agent must be known.
* String agentAddress = getAgentAddress(myOaa, "myServerAgent");
* solveParams = new IclList();
* solveParams.add(new IclStruct("direct_connect", new IclStr("true")));
* solveParams.add(new IclStruct("address", IclTerm.fromString(true,agentAddress)));
* }
*
* IclList res = new IclList();
*
* myOaa.oaaSolve(goal, solveParams, res);
*
* if (res.size() == 0) {
* throw new Exception("No answers found");
* }
*
* return res;
* }
* </code></pre>
* Note that in the above example code the agent address is retrieved only once
* since this is an expensive operation. If the method <code>callOaaMaximumPerformance</code>
* is called repeatedly it will perform better than regular direct_connect
* without the address.
* </p>
* <p>
* Finally, a method for detecting whether or not direct_connect was successful
* is possible specifying the <code>"get_direct_connect_used"</code> out parameter
* in <code>oaaSolve</code>. For example, if oaaSolve is called and one of the
* parameters is <code>"get_direct_connect_used(_)"</code>, when the solve call returns
* this parameter is replaced with <code>"get_direct_connect_used(true)"</code>
* if direct_connect was successfull and <code>"get_direct_connect_used(false)"</code>
* if direct_connect was not used or was not successfull.
* </p>
*
* <h4>Registering the agent</h4>
* Once a low-level communication connection has been established, an agent registers its
* name and published services across this connection. In this example, the agent named
* "fax_db" publishes that it can provide the service of finding a fax number given a
* person name. The default connection name for <code>comConnect()</code> and <code>
* oaaRegister()</code> should be "parent", which represents the Facilitator agent.
* <pre>
* // Then, once the connection is established, performs handshaking with the facilitator
* if (!myOaa.oaaRegister("parent", "fax_db", IclTerm.fromString("[fax_num(Person,Num)]"))) {
* printError("Could not register");
* return;
* }
* </pre>
* When the agent has finished all initializations and is ready to receive requests from
* the agent community, it must let the Facilitator know that it is ready.
*
* <pre>
* myOaa.oaaReady(true);
* </pre>
*
* That's it! Incoming requests will arrive in the function <code>
* myOAADoEvent(goal, params, answers);</code> where the agent will parse the
* goal, try to solve the request, and return 0 (failure), 1 (success), or
* more solutions in the answers list. To accomplish the goal, the agent may need
* to make use of communication requests to other members in the agent community
* using the <code>oaaSolve()</code> or other library functions.
*
* <h4>Disconnecting the agent</h4>
* The agent can disconnect all connections by invoking the
* <code>oaaDisconnect</code> method.
*
* <p>
* <hr>
* <H2>Event communication patterns for OAA primitives</H2>
* <p>
* Asynchronous communication events between client agents and a facilitator
* are at the heart of the agent library and facilitator implementations.
* Here we describe the communication message activity for the OAA
* primitives defined in an OAA library.
* <p>
* <pre>
* <li>Handshaking between Client and Facilitator at startup time:
*
* <b><i> oaaRegister(ConnectionId, AgentName, Solvables, Params) </b></i>
* client->Fac: ev_connect(AgentInfoList)
* client<-Fac: ev_connected(FacInfoList)
* <p>
* <li>Notification to facilitator that client is ready to participate:
* <b><i> oaaReady(ShouldPrint) </b></i>
* client->Fac: ev_ready(SymbolicName)
* <p>
* <li>Registration of client solvables and data declaractions with Facilitator:
* <b><i> oaaDeclare(Solvs,CPerms,CParams,Params,-Declared), </b></i>
* <b><i> oaaUndeclare(Solvs,Params,-Undeclared), </b></i>
* <b><i> oaaRedeclare(Solvs,Params,-Redeclared), </b></i>
* <b><i> oaaDeclareData(Solvs,CPerms,CParams,Params,-Declared) </b></i>
* client->Fac: ev_post_declare(Mode,Solvs,P) Mode = add,remove,replace
* if client agent is a NODE facilitator
* node->parent: ev_register_solvables(Mode,Solvs,MyName,P)
* client<-Fac: ev_reply_declared(Mode, Solvs, P, Declared)
* <p>
* <li>Delegation of simple or compound goals across agent community:
* <b><i> oaaSolve(G,P) </b></i>
* client->Fac: ev_solve(GoalId, G,P)
* If no clients can solve G
* Fac->Parent: ev_post_solve_from_fac(Id, G, P)
* Fac<-Parent: ev_reply_solved_by_fac(Id, S, G, P,Sols)
* else
* Fac->client: ev_solve(GoalId, G, P)
* Fac<-client: ev_solved(Id, Requestees, Solvers, G, P, Solutions)
* client<-Fac: ev_solved(GoalId, Responders, S, G, P, Sols)
* <p>
* <li>Delegation/manipulation of data across agent community:
* <b><i> oaaAddData(D,P), </b></i>
* <b><i> oaaRemoveData(D,P), </b></i>
* <b><i> oaaReplaceData(D,P) </b></i>
* client->Fac: ev_update_data(GoalId,Mode,D,P) Mode = add,remove,replace
* Fac->client: ev_update_data(GoalId, Mode, D, P),
* Fac<-client: ev_data_updated(GoalId, Mode, D, P, Sols)
* client<-Fac: ev_data_updated(GoalId, Mode, D, P, Responders, Sols)
* <p>
* <li>Installation/removal of triggers delegated across agent community:
* <b><i> oaaAddTrigger(D,P), oaaRemoveTrigger(D,P) </b></i>
* client->Fac: ev_update_trigger(GoalId,Mode,T,C,A,P) Mode = add,remove
* Fac->client: ev_update_trigger(GId, Mode,T,C,A,P),
* Fac<-client: ev_trigger_updated(Id,Mode,T,C,A,P,Requestees,Updaters)
* client<-Fac: ev_trigger_updated(Id,Mode,T,C,A,P,Requestees,Updaters)
* </pre>
* <p>
* @author Didier Guzzoni
* @author Adam Cheyer
* @author David Martin
* @author Ruth Lang * @version %I%, %G%
*/
public class LibOaa extends Thread {
/**
* Property name for the OAA library log4j configuration file.
*/
public static final String OAA_LOG_CONFIG_FILE = "oaa.log.config.file";
/**
* Default property value for the OAA library log4j configuration file.
*/
public static final String
OAA_LOG_CONFIG_FILE_DEFAULT = "/com/sri/oaa2/oaa_log_config.xml";
// setup logging in this static initializer
static {
System.out.println("Initializing OAA library");
// Is OAA already initialized?
Enumeration allLoggers = LogManager.getLoggerRepository().getCurrentLoggers();
boolean initLog4J = true;
if (allLoggers != null) {
while (allLoggers.hasMoreElements() && initLog4J) {
Logger logger = (Logger) allLoggers.nextElement();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -