2024年3月17日发(作者:)
JADE程序员指导
Jade集成开发环境是一个入门级的产品,但是它仍然拥有强大的功能。它提供的用户图形界
面包含了大部分SUN SDK工具,可以方便的编辑源代码,生成一个类,执行一个java程序
或java applet. Jade提供的高效率的功能可以让你为每个project生成单独的类模板,这个功
能包括生成使用AWT或 Swing APIs对话窗口。Jade可以使用Sure shot的Jive Lint支持静
态代码检查,同时也提供了入门级的调试功能。新版本提供了如下功能:允许用户在XML
编辑器中调用用户提供的函数;方法定位(method location)采用了树形视图。
1 简介
程序员指南由管理员指南作为补充,在jade/doc目录下可以找到可用的HTML文档。如果
本指南和HTML文档之间哪里出现了冲突,以经常更新的HTML文档为准。
JADE(Java Agent Development Framework)是一个软件开发框架,它可以为智能AGENT
开发多AGENT系统和遵守FIAP协议的应用程序。它包含两个主要的产品:一个是与FIPA
相适应的agent平台,另一个是开发Java agents的包。JADE是完全用JAVA编码的,如果
agent程序员想探索本框架,他/她的agents就应该按照这个程序员指南中描述的大纲用Java
编码。
这个指南是在假设读者熟悉FIPA标准,至少是熟悉Agent Management specifications(FIPA
no.23),Agent Communication Language和ACL Message Structure(FIPA no.61)的基础上编
写的
JADE是用JAVA写的由各种JAVA包构成,给程序设计者以完备的功能接口和规范的抽象
性界面,具体应用取决与任务。选择JAVA语言是因为它有许多独特的特点,尤其是在分布
式异质环境下的面向对象的编程连接方式;这些特点还包括Object Serialization, Reflection
API和Remote Method Invocation(RMI)。
JADE由如下的包构成:
-执行系统核心问题,包括必须由软件程序员扩展的Agent类,除此之外一个行为类
包含在ours子包内。行为执行agent的任务或目的。它们是可以完成不同复
杂任务或并行任务的逻辑行为单元。程序员编写行为定义agent的操作,并相互连接他们定
义agent执行路径。
子包用来根据FIAP标准规范处理ACL的。
t子包包含了一系列支持用户定义的本体和内容语言的类。单独有一个指南描述
如何使用JADE支持消息内容。尤其是包含了SL编码解码器,包含有皮
剖析器和解码器。
b包:包含了FIPA标准定义的表示Agent管理实体的所有Java类,特别是AMS
agents和DF agents,提供生命周期服务,白页和黄页服务。子包
entManagemnt包含FIPA-Agent-Management Ontology和所有表示它的概
念的类。而子包entManagemnt包含JADE的Agent管理的扩展名(比
如,为sniffing messages,controlling the life-cycle of agents,…),包括本体和表示它的概念
所有类。子包pection包含用于JADE工具(比如,sniffer和Introspector)
间引用域和JADE核心的概念。子包ty包含用于移动通信的概念。
包:包含了一系列有助于创建GUIs的普通类,用于显示和编辑Agent-Identifiers,
Agent Descriptions,ACLMessages,…
包:包含一个为容易地与JADE框架整合,每一个MTP都应该执行的Java接口,
和一系列这些协议的实现。
包:包含模拟标准交互协议(例如:fipa-request, fipa-query, fipa-contract-net,
fipa-subscribe和由FIPA定义的其它一些协议)的类,和帮助程序员创建他们自己的协议的
类。
FIPA包包含由FIPA为基于IIOP的消息传输定义的IDL模块。
最后,r包提供JADE高层次封装功能,这可以将JADE的用法作为库,从而使
外部的Java程序启动JADE agents和agent容器(见3.8节)。
JADE还包括一些工具,用来简化平添管理和应用开发。每个工具包含在的一个
单独的包内。目前,以下工具是可用的:
Remote Management Agent(RMA):作为平台管理和控制的图形控制台。第一个RMA实例
可以通过命令行选项(“-gui”)来启动,但是以后可以激活多个GUI。通过简单的用多台调
频发射机向所有RMAs播送一个事件,JADE在多RMAs保持一致性。而且,RMA控制台
能启动其它JADE工具。
Dummy Agent:是一个监控和调试工具,由图形用户界面和潜在的JADE agent构成。用GUI
可能写出ACL消息,并将消息发送给其他agents;也可能显示所有发送或接收到的ACL消
息,以及为了便于agent通话记录和复述的时间戳信息。
Sniffer:是一个在ACL消息传输时可以截取ACL消息,并用类似与UML程序表的符号将
消息图形式显示的agent。它有助于通过观察agents如何交换ACL消息来调试你的agent社
会。
Introspector:可以监控agent生命周期,它交换的ACL消息和执行的行为的agent。
DF GUI:是一个完全的图形用户界面,被默认的JADE DF使用,也可以被其它每一个用户
可能需要的DF使用。以这种方式,用户可能创黄页域和子域的复杂网络。GUI可以用简单
直觉的方式控制DF的知识库,使DF和其它DF联合,和远程控制(注册/注销/修改/搜索)
DF父亲DF和孩子DF的知识库(执行域和子域网络)。
LogManagerAgent:允许在运行时设置日志信息,如日志水平,对于用Java日志的JADE和
程序应用的特殊类来说。
SocketProxyAgent:是一个普通的agent,作为JADE平台和普通TCP/IP连接间的双向通道。
仔细考虑JADE所有的传输服务,ACL消息被转换成简单的ASCII字符串并通过套接字连
接传输。反过来,ACL消息可以通过TCP/IP连接到JADE平台传送。这个agent是有用的,
例如,处理网络防火墙或在Web浏览器中提供与Java applets交互的平台。
JADE
TM
是一个由CSELT
3
注册的商标。
JADE由五部分组成:混淆器、加密器、封装器、类编辑器和输出工具。
混淆器能够保护软件使之不被反编译。它通过混淆类文件使得反编译无效,并把敏感的名字
指代变成另一个名字空间,这样可以使反编译的结果毫无疑义。混淆器有“完全”和“快速”两
种选项,能处理任何Java产品包括API,应用程序和小程序。它遵从Java虚拟机规范。
加密器隐藏和加密用户产品的主类,处理的结果是一个类文件。它为编码和反编码提供了一
个界面。JADE有一些类用DES完成这个界面。加密和解密的过程对于用户来说是透明的。
用户感觉不到使用原始的主类和使用处理后的结果类有什么不同。当然,用户也可以不加密
地处理这些主类和产品。
封装器不仅仅处理类文件,它还隐藏和加密在产品根目录下的其它文件。它封装产品并只产
生一个类。软件厂商把这个类销售给用户,用户将运行这个类文件,安装买来的软件。封装
器使用和加密器相同的安全类。
对于高级Java开发人员,JADE还提供了一个叫做“类编辑器”的工具。它能够直接修改类的
代码字节。类编辑器也是一个独立的应用程序,它将把所有的结果生成到jar格式的的文件
里。
输出工具把所有的结果生成到一个jar格式的文件中,这个文件能够被其它的压缩工具如
Unzip, Winzip和Jar处理。
JADE是由上面的工具集成起来的应用程序,它由纯Java语言编写。这些应用既能够在命令
行下运行,也可以在图形界面环境下交互使用。用户可以分别使用这些功能,也可以按下图
中的序列运行一组特征。JADE允许用户把配置参数存到一个文本文件里,并可以编辑它。
这样,下一次只要装入该配置文件就可以在相同的参数下运行了。
2 JADE的特征
下面是JADE为agent程序员提供的特征列表:
分布式的agent平台:agent平台可以分布在许多主机之中(假设他们通过RMI连接)。
在每台主机上只有一个Java应用程序,因此也就只有一个Java虚拟机运行。Agents
以Java的思路执行,存在于Agent容器中,agent容易为在运行时为agent执行提供
支持。
图形用户界面:从远程主机来管理若干agents和agents容器。
调试工具:帮助开发基于JADE的多agents软件程序。
通过行为模型支持agent活动的多重、平行和并行执行。JADE以非优先权的方式安排
agent的行为。
遵循FIPA的agent平台:它包括AMS、DF和ACC。所有这三个组成都在agent平台
启动时自动激活。
许多遵循FIPA的DFs可以在运行时启动以执行多域程序,一个域是指一系列逻辑
agents,它们的服务通过一个共同的facilitator发布。每个DF继承一个GUI和所有由
FIPA定义的标准能力(例如,注册,注销,修改和搜索agent描述的能力,DF网络内
联盟的能力)。
在同一个agent平台内有效传输ACL消息。事实上,消息被编译成Java对象而不是字
符串的形式进行传输,以避免集结或未编组的程序。当跨越平台边界时,消息自动转换
成/自基于FIPA语法、编码和传输协议。这种转换对只需要处理Java对象的agent的
执行者是很明显的。
备用的FIPA交互协议库。
Agent在AMS上的自动注册和注销。
遵循FIPA的命名服务:在启动agents时从平台上获取它们的GUID(Globally Unique
Identifier)。
支持程序定义的内容语言和本体。
InProcess接口允许外部程序启动自治agents。
3 用JADE创建多agent系统
本章描述支持多agent系统开发的JADE类。JADE遵循句法规则,有写地方遵循FIPA规范
的语法
3.1代理平台
FIPA定义的一个agent平台的标准模型,如下图所示:
AMS是管理控制进出和使用AP的agent。一个平台上只能有一个AMS。它提供百页和生命
周期服务,维护AID目录和agent状态。为了获得有效的AID,每个agent都必须在AMS
上注册。
DF是平台上提供默认黄页服务的agent。
消息传输系统也叫agent通信通道(ACC),由软件构成在平台内负责所有的消息交换,包
括来自或发送到远程平台的消息。
JADE完全遵守这个参考架构,当一个JADE平台被启动,AMS和DF立刻被创建,ACC
模块也设置为允许消息通信。Agent平台可以分布在不同的主机上。但一台主机上只能有一
个应用程序即一个JAVA虚拟机运行。每个JVM是一个基本的agents容器,它为agent的执
行提供完全运行时环境,并允许若干agents在同一台主机上并行执行。创建主容器或
front-end,它是AMS和DF存在以及RMI注册的agent容器,被JADE内部使用。其它agent
容器与主容器相连,为任何JADE agents的执行提供完全运行时环境。
根据FIPA规范,DF和 AMS agents通信使用FIPA-SL0内容语言,fipa-agent-management
本体,fipa-request交互协议。JADE为所有这些部分提供编译执行:
SL0内容语言是由c类执行的,使用此语言的自动功能可以
用getContentManager().registerLanguage(new SLCodec(0))方法添加到任何agent。
本体概念(除了执行的AID)被entManagement包中
类执行。FIPAManagementOntology类定义了与本体符号一致的词汇。用下面的代码使
用本体的自动能力可以添加到任何agent:
最后,fipa-request交互协议作为ready-to-use行为执行,存在于包内。
3.1.1FIPA-Agent-Management本体
每一个使用fipa-agent-management本体的类都是一个简单的属性集合,根据基于表示FIPA
的fipa-agent-management本体概念的模型框架,用公共方法读写它们。使用下面的规定:为
类的每个属性的attrName命名和attrType指明类型,有两种可能的情况:
1) 属性类型是一个单一值,则它们由attrType getAttrName()读取,由void setAttrName
(attrType a)写入,每次调用setAttrName()后的执行结果将重写以前的属性值。
2) 属性类型是一系列的值,则用void addAttrName(attrType a)方法插入一个新值,
void clearAllAttrName()方法移除所有的值(列表变为空)。读取则用返回Iterator对象的
Iterator getAllAttrName()方法,允许程序员查看列表并将列表元素设置合适的类型。
参考HTML文档查看这些类和它们的接口的完整列表。
3.1.1.1本体的基本概念
包之中包含一系列类,它们是每个本体的通用部分,如Action,
TrueProposition, Result, , … 正如3.6节所讲的BasicOntology可以加入任何用户定义的本体。
注意:Action类被用于表示行为。它有一对方法来set/get行为者的AID(如应该表现行为
的agent)和行为本身(如注册/注销/修改)。
3.1.2 简化API以访问DF和AMS服务
目前为止描述的JADE特征允许FIPA系统agents和用户定义agents之间通过简单的发送和
接收由标准定义的消息进行交互。
然而,因为那些交互被充分的标准化,因为它们非常一般,下面的类可以用简化的接口成功
完成这一任务。
Agent类用两种方法得到平台默认DF和AMS的AID:getDefaultDF()和getAMS()。
3.1.2.1DF服务
ice执行了一系列与标准FIPA DF服务通信的静态方法(如黄页agent)。
它包括的方法有:从DF请求注册,注销,修改和搜索行为。这种方法的每个版本带有所有
需要的参数,以及它们的子集,省略的参数以缺省值填充。
注意:这些方法block每一个agent活动直到该活动成功执行或抛出一个
ception异常(如因为DF接收到一个failure消息),即直到对话结束。
另外,有一些情况,以non-blocking方式执行这些任务更方便。在这些情况下,应该用
eREInitiator或iptionInitiator(见3.4.12节)关联
createRequestMessage(), createSubscriptionMessage(), decodeDone(), decodeResult()
和DecodeNotification()方法,以方便到/从DF发送/接收的消息的准备和解码。下面这段
代码解释了agent与默认的DF协商的情况:
基本的DF语句:
3.1.2.2AMS服务
这个类是DFService的二重类,访问由标准的FIPA AMSagent提供的服务,它的接口完全相
当于一个DFService接口。
注意:在分别调用setup()方法前和takeDown()方法后,JADE用默认的AMS自动调
用注册和注消方法,所以对一般程序员来说不需要调用它们。
然而,在某些情况下,程序员可能需要调用它的方法。例如:当一个agent想在远程agent
平台的AMS上注册时,或者当一个agent想通过添加私有地址到它的地址序列从而修改它
的描述时,…
3.2 agent类
Agent类对用户定义的agents来说表示一个通用的基本类。因此,从程序员观点来说,JADE
agent简单地说就是一个从基本Agent类扩展来的用户定义的Java类的实例。这就意味着对
在agent平台上完成基本交互的特征(注册、配置、远程管理、…)和可以被调用执行agent
规定的行为的一系列基本方法(如send/receive message, use standard interaction protocols,
register with several domains,…)的继承。
Agent的计算模型是多任务的,任务(或行为)是并发执行的。Agent提供的每项功能/服务
应该作为一个或多个行为执行(参考3.4节行为执行)。调度程序对基本agent类是内部的,
而对程序员来说是隐藏的,自动管理行为的调度。
3.2.1agent的生命周期
根据FIAP规范中的AP生命周期理论,JADE agent可以处于若干状态中的一种,如图3所
示,下面将详细介绍:
创建:创建agent对象,但没有在AMS上注册,也没有名字和地址,不能其它agents
通信。
活动:agent对象在AMS上注册,有一个合格的名字和地址,可以访问所有JADE特征。
挂起:agent对象当前被停止。它的内部线程被暂停,没有agent行为被执行。
等待:agent对象被阻塞,等待什么。它的内部线程处于睡眠状态,当一些条件满足时
被唤醒(典型地是消息到达)。
删除:agent明确地死亡。内部线程终止它的执行,agents不再注册在AMS上。
转移:当移动agent向新的地址迁移时进入这个状态。系统连续缓存消息,之后将发往
它的新的地址。
Agent类提供公共方法使其在各种状态间转换,这些方法从FIPA规范agent管理中的有限状
态机(the Finite State Machine)内的适当的转换中获取它们的名字。
例如,doWait()方法把agent从活动的状态转变为等待状态,doSuspend()方法把agent
从活动或等待状态转变为挂起状态,…参考agent类的HTML文档,它提供这些doXXX()
方法的完整列表。
注意:只有在活动状态下,代理才能执行它的行为(即它的任务)。小心:如果任何一个行
为调用doWait()方法,那么整个agent和它的所有活动都将被阻塞,而不仅仅是调用该方
法的行为。block()方法是Behaviour类的一部分,它允许单个agent行为挂起(详见3.4
节的行为用法)。
3.2.1.1启动agent执行
JADE框架根据一下步骤控制新的agent的诞生:执行agent构造器,给agent赋一个标识符
(见类的HTML文档),在AMS上注册,进入活动状态,最后执行setup()
方法。根据FIPA规范,agent标识符有一下属性:
一个全局唯一的名字。默认情况下由JADE用本地名字串联组成——例如,命令行提供
的agent名字加上“@”,再加上本地AP标识符——例如 JADE RMI registry> ’/’ ‘JADE’);还有一种情况在命令行说明平台名字,本地名字加上 ‘@’加上规定的平台名字串联组成agent名字。 一系列agent地址。每个agent继承本地agent平台的传输地址。 一系列解释器,例如agent注册用的白页服务。 setup()方法是应用程序定义的agent活动开始的点。程序员要想初始化agent,必须执行 setup()方法。setup()方法执行后,agent就已在AMS上注册,它的AP状态处于活动状态。 程序员应该用以下初始化程序: —(可选的)如果需要,修改AMS注册数据(见3.1.2) —(可选的)如果需要,设置agent的描述和它提供的服务,以及在一个或多个域注册agent, 比如DFs(见3.1.2) —(必选的)用addBehaviour()方法添加任务到准备好的任务队列。 一旦setup()方法结束,就安排这些行为。 setup()方法给agent至少添加一个行为。setup()方法结束后,JADE自动执行准备好的任务队 列中的第一个行为,然后用无优先的循环调度方法切换到队列中的其它行为。Agent类的 addBehaviour(Behaviour)和removeBehaviour(Behaviour)方法可以用于管理任务队列。 3.2.1.2停止agent执行 任何行为都可以调用te()方法来停止agent运行。 当agent将要进入DELETED状态时,运行wn()方法,例如agent被销毁的 时候。takeDown()方法可以被程序员重写以执行任何必要的清理。当这个方法执行的时 候,agent仍然注册在AMS上,从而可以给其它agents发送消息,但是takeDown()方法完 成后,agent将注销,它的线程也将被销毁。这个方法想要的目的的执行程序指定的清除操 作,如DF agents上的注销。 3.2.2 agents间通信 Agent类也提供一系列agents间通信的方法。根据FIPA规范,agents通过异步消息传输通 信,ACLMessage类的对象是交换的有效载荷。也可以参见3.3节对ACLMessage类的描述。 FIPA定义的一些交互协议如ready-to-use行为一样可以为agent活动指定;它们是 包的一部分。 ()方法允许发送ACLMessage。receiver字段的值表明接收anget的IDs列表。 该方法在agent所在的地方是完全透明的,比如作为本地的或远程的agent,平台认真选择 的最合适的地址和传输机制。 3.2.2.1访问私有消息队列 平台通过一个agent将所有收到的消息加入agent私有队列。(从JADE2.5开始)队列的默认 规模是无限制的,但是,有限资源的事情下,可以通过setQueueSize()方法改变缺省值。 可以通过若干中访问模式从这个私有队列中得到消息: —可以以控制(使用blockingReceive()方法)或非控制方式(用receive()方法)访问消息 队列。必须慎用控制方式,因为它会引起所有agent活动挂起,尤其是它所有的行为。当队 列中没有请求消息时,非控制方式立刻返回null。 —两种方法都可以通过pattern-matching能力扩展,传入一个描述请求的ACLMessage的方 式的参数。3.3.4描述的MessageTemplate类。 —控制访问可以有一个时间间隔参数。它是长整型的,用来描述为请求消息agent活动应该 保持控制等待状态的最大毫秒数。如果消息到达前超过了间隔时间,该方法返回null。 —两个行为ReceiverBehaviour和SenderBehaviour可以用来安排请求接收或发送消息的agent 任务。 3.2.3 图形交互界面agent 一个构建多agent系统的应用程序仍然需要与用户交互。所以,有必要提供GUI,至少是为 程序中的一些agents提供。这一需要仍引发了一些问题,是由agent的自治属性和普通 GUI 的反作用属性之间的不匹配引发的。这些是JADE需要提高一些问题。当JADE被使用,JADE agents的thread-per-agent并行模型必须和Swing并行模型一起工作。 3.2.3.1 Java GUI并行模型 在一个JAVA虚拟机上有一个单一线程,成为Event Dispatcher Thread,它的任务是从System Event Queue(它是ueue类的一个实例)中连续选择事件对象(比如: nt类的实例)。然后其它事情中的事件调度线程,调用各种注册到事件资 源的监听者。重要观察的是所有的事件监听者都在一个单一的控制线程(the event dispatcher)中执行;因为这遵循已知的规则,所以每个事件监听者的执行时间应该缩短(小 于0.1s)以确保界面响应。一个重要的Swing特征是Model/View系统来管理GUI更新。 Swing控制器有某种状态(JCheckBox有checked标识,JList持有元素,等等),这状态保 存在Model对象(DefaultButtonModel类,ListModel类,等)中。model类提供修改状态 的命令(比如,检测或不检测checkbox,list中添加或去除元素,等),并且Swing建立了 告知机制,更新可见的GUI的外观以反映状态的改变。所以JCheckBox对象可以在两种情 况下改变它的外观: 从用户接收到事件(如,一个MouseClick事件) 程序中的其它部分改变了和JChecBox相关的model对象 正如在JAVA指南(JFC/Swing trail、Threads和Swing部分)里指出的一样,Swing框架不 是线程安全的,因此任何更新GUI元素的代码必须在事件调度线程之内执行;因为改变一 个model对象会引发GUI的更新,所以按照上面所说的,model对象也必须由事件调度线程 来控制。Swing框架提供了一个简单但通用的方式将一些用户定义的代码传给Event Dispatcher thread:SwingUtilities类包含了两个静态方法,接受Runnable对象,用 RunnableEvent将其封装,并把它放到System Event Queue中。invokeLater()方法把Runnable 放入System Event Queue中,并立即返回(此行为类似于异步交互线程调用),而 invokeAndWait()方法把Runnable放入System Event Queue中,就休眠直到Event Dispatcher thread处理了RunnableEvent(此行为类似于同步交互线程调用)。而且,invokeAndWait()方 法可以捕获由Runnable对象抛出的异常。 3.2.3.2为响应一个GUI事件做一次ACL信息交换 当一个agent被给一个GUI时,则因为用户的动作,要求该agent发送一个消息(例如,用 户点击了pushbutton)。ActionListener按钮将在事件调度线程之内运行,而() 方法应当在agent线程之内调用。 所以: 在事件监听者中,给agent添加一个新的行为,以执行必须的通信。 如果做的通讯是一个简单的信息传送操作,可以使用SenderBehaviour类,事件管理者将包 括一行代码如下: aviour(new SenderBehaviour(msgToSend )); 如果通讯操作是一个信息接收操作,同样地可以用ReceiverBehaviour类: aviour(new ReceiverBehaviour(msgToRecv )); 更一般地说,当用户在GUI上操作时,一些复杂的会话(例如,遵循交互协议的整个交互 过程)就会启动。在一次,这个方法给agent添加一个新的行为;这个行为将为交互协议扩 展预先定义的JADE behaviours类,或将是一个定制的复杂行为。 下面的代码是从JADE RMA management agent中抽取出来的。当用户要创建一个新的agent 的时候,他/她就在RMA GUI上操作(通过菜单栏,工具栏或弹出菜单)执行 StartNewAgentAction对象,该对象调用rma类的newAgent()方法。这一方法代码如下: AMSClientBehaviour类是ram类的一个私有内部类,它扩展自FipaRequestInitiatorBehaviour, 与AMS agent间使用fipa-request交互协议。在这种情况下,addBehaviour()调用和特定行为 类的添加都被完全封装到rma类内。RMA GUI(主要是action类)的各种类都参考RMA agent, 并用它来调用方法,如newAgent()。注意:像newAgent()之类的方法并不是真正属于该agent 的,因为它们不以任何方式访问该agent。所以,它们是为了从外部(不同的执行线程)调 用而设计的:接下来,这些方法将称为external methods。 一般来说,一个外部软件组件维持关于agent的直接对象不是件好事,因为这个组件可以直 接调用该agent的任何公共方法(而不仅仅是外部的方法),跳过异步消息传输层,把自治 agent转换为服务对象,从属于它的调用者。更好的方法是将所有的外部方法集中到一个接 口,再由agent类使用。这样涉及到该接口的对象将被传送到GUI,这样只有外部方法可以 从事件管理者那里调用。下面的代码将解释这个方法; 上面的例子,GUI智能调用RMA agent的外部方法。 3.2.3.3收到ACL消息时修改GUI Anget可以通过ACL消息从其它agents获取消息:FIPA通信原语inform就是用于此目的。 如果该gent有一个GUI,它会想经常通过改变可视的GUI外观和它的用户交流新的信息。 根据Model/View模式,新消息应该用于修改一些model对象,Swing会自动更新GUI。用 于读取消息的e()操作在agent线程中执行,但是对Swing model对象的修改必须 从Event Dispathcer线程中执行。 所以: 在agent行为中,将所有对GUI model对象的访问封装成一个Runnable对象,用 XXX()将Runnable提交给Event Dispathcer线程。 例如,当一个新的agent在JADE平台诞生,AMS就会发送inform消息给所有的活动的RMA agents;它们当中的每个agent都要更新它的AgentTree,添加代表新agent的节点。rma类 掌管着(内部的和私有的)AMSListerner类的行为,它不断从AMS接收inform消息,并将 消息发送给合适的内部事件管理者(它基本上是一个简单地分布式的ACL消息上的事件系 统)。与agent-born事件相对应的管理者有下面的代码: 正如从上面代码中看到的,对agent树的所有访问都被封装到Runnable中,用 Later()方法提交到Event Dispatcher线程执行。整个Runnable创建和提 交过程都包含在MainWindow类的addAgent()方法中,这样rma agent不直接处理Swing调 用(它甚至不必引用Swing相关类)。 如果我们将整个MainWindow作为一个活动对象,它的线程是Event Dispatcher线程, addAgent()方法明显的是一个外部方法,这个方法准确反映了上面部分中用到的技术。但是, 因为GUI没有被看做自治软件组件,所以是否选择使用外部方法只是一个软件结构问题, 没有特殊的概念意义。 3.2.3.4在JADE上构建GUI可用的agents 使agents拥有GUI是件很普通的事情, JADE包含nt类以达到这个目的。 这个类是类的简单扩展:在启动(setup()方法执行的时候)的时候,它建立 ad-hoc behaviour的实例,管理nt事件对象的队,该队列可以被其它线程接收。 当然,这个行为对程序员是隐藏的,他们只需要实现和每个事件相关的应用程序代码。详细 地说,必须执行下面的操作。 一个线程(尤其是GUI)希望通知一个事件给一个agent,它就应该创建一个新的 nt类型的对象,并将它作为参数传给nt对象的postGuieEvent 方法的调用。postGuiEvent()方法调用后,agent通过唤醒所有它的活动行为作为反应,尤其 是上面提到使agent线程执行onGuiEvent()方法的行为。注意:GuiEvent对象有两个必须的 属性(the source of the event和an integer identifying the type of event)和一个可以添加到event 对象的可选参数列表。 作为结果,希望从另一线程(尤其是它的GUI)接收事件的agent应该定义它想接收的事件 的类型,然后执行onGuiEvent()方法。一般来说,这个方法是一个大的循环,每种情况对应 一种事件类型。JADE发布的mobile例子就是一个很好的例子。 为了进一步解释前面的概念,下面给出一些关于MobileAgent例子的有趣的代码段: 3.2.4带参数的agent和启动代理 参数列表可以传给agent,也可以通过调用方法Object[] getArguments()进行检索。注意:参 数是瞬时的,不能与agent一起迁移,也不能随agent克隆。 有三种方式启动agent: 使用管理者指南中描述的语法,在命令行输入一张agent列表,括号中的参数可以传给 每个agent。这是最普通的选择,这个选择也是与agent自治的理论要求最匹配的。 如管理员指南中描述的,用户可以通过使用RMA(Remote Monitoring Agent)GUI启 动agent。括号中的参数可以传给每一个agent。 最后,任何外部Java程序可以用3.8节中描述的InProcess接口启动agent。 3.3 agent通信语言(ACL) 类ACLMessage代表可以在agents间交换的ACL消息。它包含一系列FIPA规范定义的属 性。 希望发送消息的agent应该创建一个新的ACLMessage对象,然后用恰当的值填充属性,最 后调用方法()。同样地,希望接收消息的agent应该调用receive()或 blockingReceive()方法,Agent类使用的两种方法在3.2.2节中都介绍了。 通过在agent任务队列加入ReceiverBehaviour和SenderBehaviour行为,发送或接收消息也 可以作为独立的agent活动。 所有ACLMessage对象的属性都可以通过set/get 按FIPA规范定义的参数名字命名。那些参数类型是一个值的集合(如receiver)的参数可以 通过方法add/getAll 所有的值返回Iterator。注意:当没有设置属性的时候,所有的get()方法返回null。 此外,这个类也定义了一个常量集合,用于说明FIPA原语,如REQUEST, INFORM,等。当 创建一个新的ACLMessage对象的时候,这些常量中的一个必须传递给ACLMessage类构造 器,以选择消息原语。reset()方法重置所有消息字段的值。 toString()方法返回代表消息的字符串。这个方法应该只用作调试目的。 3.3.1支持消息回复 根据FIAP标准,必须按照一系列成熟的规则进行消息回复。如,为in-reply-to属性设置恰 当的值,使用同样的conversation-id,等。在这项任务中,JADE ACLMessage类的createReply() 方法帮助程序员。这个方法返回一个新的ACLMessage对象,该对象是对当前消息的有效回 复。程序员只需要设置程序指定通信动作和消息内容。 3.3.2支持JAVA传送字节 有些程序可能从传送ACLMessage消息之外的字节序列获益。典型的用法就是通过使用Java 序列化在两个agents之间传送Java对象。在这个任务中,ACLMessage类通过 setContentObject()和getContentObject()方法支持程序员,这两个方法自动激活Base64编码用 法。参考JADE API的HEML文档,在examples/Base64目录下找到关于这一用法的例子。 从JADE2.5开始,增加了两个方法允许到/从ACLMessage的内容设置/获取字节序列: get/setByteSequenceContent()。在某些情况下(尤其是当ACLMessage编码成字符串格式), 字节序列可能创建不兼容的和不可回复的消息内容,使用这两个方法的时候一定要格外小 心。 3.3.3ACL编码解码器 在一般情况下,agent从来不需要显式调用ACL消息的编码解码器,因为它由平台自动调用。 然而,对一些特殊情况,需要显式调用的时候,程序员应该用StringACLCodec类提供的方 法以字符创格式对ACL消息进行解析和编码。 3.3.4 MessageTemplate类 JADE行为模型允许agent执行若干并行任务。然而也应该提供任何agent执行多个并发会话 的能力。因为所有引入的消息队列由所有的agent行为共享,所以必须使用基于模式匹配的 访问模式访问该队列(见3.2.2.1)。 MessageTemplate类允许建立模式以匹配ACL消息。用这个类的方法,程序员可以为每个 ACLMessage属性创建模式。基本模式可以用AND, OR和NOT进行合并操作,以建立更复 杂的匹配规则。以这样的方式,引入ACL消息队列可以通过模式匹配而不是FIFO访问。 用户可以定义程序特定的模式,扩展MatchExpression接口以提供新的match()方法用于模式 匹配阶段。 在examples包的MessageTemplate目录下的WaitAgent例子,说明了创建程序特定 MessageTemplate的创建方法: 3.3.5基于主题的通信 从JADE3.5版也支持基于主题的通信,除了给一个或多个接收者(addressed by name)发送 消息,它还可以发送关于某一给定主体的消息。这种消息将被发给所有在该主题下注册它们 的兴趣的agent。如果没有agent在一个主题下注册它的兴趣,给该主题发送消息怎没有任 何影响(例如,收不到FAILURE回复)。应该注意一点,发送者agent不需要知道任何关于 有哪个agent注册到该主题的信息。 基于主题的通信由anagementService实现,因此必须激活平台上 所有的容器(见JADE管理员指南上的JADE Kernel services)。 为了有一个完全一致的API使得既能发送消息给既定的接收者又能发送关于给定的主题消 息,主题由AID对象表示。TopicManagementHelper接口包含在ing包内, 提供创建主题AIDs的便利方法,如果给出一个AID表示一个主题(isTopic()),也负责进行 检查。给一个既定的主题发送消息,只要把主题AID添加到消息接收者中就可以了。因此 一个关于“JADE”主题的消息可以通过下面这段代码来发送: 注册关于一个既定主题的接收消息通过TopicManagementHelper接口的register()方法来实 现,例子如下: 典型地,当注册接收关于某一主题的消息时,处理这些消息的适当的行为要添加到agent中。 MatchTopic()库方法已被添加到MessageTemplate类以便于关于某一给定主题的模板匹配消 息的创建,例子如下: 3.4 agent任务,执行agent行为 一个agent必须能够执行若干个并行任务以相应不同的外部事件。为了有效管理agent,每 个JADE agent由一个单独的执行线程组成,将所有任务建模,用Behaviour对象执行。多线 程agent也可以执行,但JADE没有提供特殊的支持(除了同步的ACL消息队列)。 希望执行明确agent任务的开发者应该定义一个或多个Behaviour子类,实例化它们,并将 它们添加到agent任务列表。Agent类必须由agent程序员进行扩展,方法有两种: addBehaviour(Behaviour)和removeBehaviour(Behaviour),可以用来管理特殊的agent的准备 好的任务队列。注意:行为和子行为可以在需要的时候随时添加,而不是只在() 方法中。添加行为应该被视为在agent内产生新的(合作的)执行线程。 由基础Agent类执行的,对程序员隐藏的安排程序,在准备好的队列中有效的所有行为之间 按照一个循环的没有优先权的排序策略执行。执行一个Behaviour驱动的类直到它释放控制 权(当action()方法返回时发生)。如果放弃控制权的任务仍然没有完成,将把它安排在下一 轮。一个行为也可以进入休眠,等待一个消息的到达。详细地说,agent调度程序执行出现 在准备好的行为队列中的每个行为的action()方法;当action()方法返回时,调用done()方法 来检查该行为是否完成了它的任务。如果完成了,该行为对象从队列中移除。 行为就像合作的线程一样工作,但是没有堆栈保存。因此,整个计算状态必须维持例如各种 Behaviour和它的相关Agent。 为了避免一个活动等待消息(浪费CPU时间),允许每个单独Behaviour休眠它自己的计算。 一旦action()方法返回,block()方法就将行为放入休眠行为队列中。注意:调用block()方法 后休眠效果并不能马上达到,但是只要action()方法返回后。一旦一个新的消息到底,所有 休眠的行为就要从新安排,因此,程序员必须注意,如果一个行为对到达的消息没有兴趣, 就要再次休眠该行为。而且,一个行为对象可以通过传递间隔时间值给block()方法使自己 休眠有限的时间。未来JADE的释放,可能要考虑更多唤醒事件。 由于为agent行为选择了无优先权的多任务模型,agent程序员必须避免使用无端回线,甚 至是action()方法内执行太长的操作。记住:当一些行为的action()正在执行的时候,其它行 为不能继续直到该方法结束(当然,这只是相对于同一个agent的行为来说的:其它agents 的行为在不同的Java线程中运行,仍然可以独立执行)。 除此之外,因为没有堆栈竞争存在,每次action()方法都从开始执行:没有方法能在行为的 action()方法中间打断它,分配CUP给其它行为,从原始行为开始的地方启动它。 例如,假设一个特定的操作op()一步完成太长,所以就分成三个子操作,命名为op1(),op2() 和op3()。为了达到期望的功能,行为运行时必须第一次调用op1(),第二次调用op2(),第 三次调用op3(),然后该行为必须被标记为结束。代码如下: 按照这个方法,agent行为可以描述为有限状态机,维持整个状态在不同的实例变量中。 当处理复杂的agent行为(作agent交互协议)时,用外部状态变量可能比较繁琐;所以JADE 也支持合成技术,由各个简单的行为来建立更复杂的行为。 该框架准备好提供使用Behaviour子类,从而可以包含子行为并按照某些策略执行它们。例 如:提供的SequentialBehaviour类,由每个action()引导,它一个接一个的执行它的子行为。 下面的图用UML类图示JADE行为: 从基础类Behaviour开始,类的层次在JADE框架的ours包中定义。 下面完整地介绍所有这些类。 3.4.1 Behaviour类 这个抽象类为模拟agent任务提供了一个抽象基础类,为行为调度创建了基础,因为它允许 状态转换(如:starting, blocking,restarting a Java behaviour object)。 block()方法允许暂停一个行为对象直到某个事件发生(典型地,直到消息到达)。这个方法 不影响agent的其它行为,从而能够更好的控制agent的多任务处理。这个方法把行为放入 暂停的行为队列中,一旦action()返回就执行。一旦新的消息到达,所有暂停的行为就重新 排序。而且,行为对象可以通过传递时间间隔值(你毫秒为单位)给block()方法,使自己 暂停有限的时间。在将来的JADE版本,将考虑更多的唤醒事件。一个行为可以通过调用它 的restart()方法显式启动。 总之,当下列三个条件之一发生时,一个休眠的行为可以重新执行: 1. 这个行为所属的agent收到一条ACL消息。 2. 和这个行为相关的时间间隔由先前block()调用到期。 3. 这个方法的restart()方法显式调用。 该Behaviour类也提供了两个占位符方法,命名为onStart()和onEnd()。当有些行为在运行时 行为执行之前或之后执行时,用户可以通过定义子类重写这些方法。 onEnd()返回一个int代表行为结束值。 应该注意:onEnd()在行为完成和并从agent行为池中移除之后调用。因此在onEnd()内调用 reset()是不能循环重复由该行为代表的任务的;除此之外,该行为应该再次添加到agent,例 子如下: 这个类也提供了一对方法来为行为获取和设置DataStore。DataStore是对行为间交换数据非 常有用的仓库,例如,eREInitiator/Responder类所做的。注意:当行为重置 时,DataStore清空,所有包含的数据丢失。 3.4.2类SimpleBehaviour 这个抽象类模拟简单的原子行为。它的reset()方法按默认情况来,但是用户可以定义子类重 写该方法。 3.4.3类OneShotBehaviour 这个抽象类模拟原子行为,只能执行一次,不能暂停。所以,它的done()方法总是返回true。 3.4.4类CyclicBehaviour 这个抽象类模拟原子行为,必须被永远执行下去。所以它的done()方法总是返回false。 3.4.5类CompositeBehaviour 这个抽象类模拟有一些其它行为(children)组成的行为。所以执行这个行为的真正操作不 是在行为本身定义的,而是在它的孩子中的,符合行关心的是根据已有的策略对孩子的调度 安排。 尤其是CompositeBehaviour类只给children的调度安排提供一个通用的接口,而不定义任何 调度安排策略。这个调度安排策略必须由子类(SequentialBehaviour,ParallelBehaviour和 FSMBehaviour)定义。因此,一个好的程序设计只用CompositeBehaviour子类,除非需要 一些特殊的children调度安排策略(比如,PriorityBasedCompositeBehaviour应该直接扩展 CompositeBehaviour)。 注意:这个类从JADE2.2重新命名了,之前叫做ComplexBehaviour。 3.4.6类SequentialBehaviour 这个类是一个CompositeBehaviour,它按顺序执行它的子行为,当所有的子行为执行完毕, 它也就终止了。当一个复杂的任务可以表示为一系列原子步骤的时候使用这个类。 3.4.7类ParallelBehaviour 这个类是一个CompositeBehaviour,它并行执行它的子行为,当关于它的子行为的一个特殊 条件满足时,它就终止。在这个类的构造器中指明的恰当的变量用来创建ParallelBehaviour, 当它所有的子行为完成的时候,它的子行为当中的任何一个终止的时候或者用户定义的N 号子行为完成的时候,它就结束。当一个复杂的任务可以表示为一个并行替代操作的集合和 一些子任务终止条件的时候使用这个类。 注意:这个类从JADE2.2重新命名,以前成为NonDeterministicBehaviour。 3.4.8类FSMBehaviour 这个类是一个CompositeBehaviour,根据用户定义的有限状态机制执行它的children。详细 地说,每个child代表FSM的状态里要执行的一个活动,用户可以定义FSM状态间的转换。 当状态S i 相对应的child完成时,它的终止值(由onEnd()方法返回的)用来选择转换机制, 到达新的状态S j 。在下一轮中,将执行与S j 相对应的child。FSMBehaviour的一些children 可以作为最后状态注册。这些孩子当中的一个完成后,该FSMBehaviour终止。 参考JADE APIs的javadoc文档,了解关于如何在运行时间或静态编译时间描述有限状态机 的详细描述。 3.4.9类WakerBehaviour 这个抽象类执行one-shot任务,必须在规定的间隔时间到期后只能执行一次。 3.4.10.类TickerBehaviour 这个抽象类实行cyclic任务,必须周期性地执行。 3.4.11例子 为了进一步说明前面的概念,下面举例子说明。说明两个agent的实现,它们分别发送和接 收消息。AgentSender行为由SimpleBehaviour类扩展而来。它发送一些消息给接收者。 AgentReceiver(也扩展自SimpleBehaviour类)代替行为说明接收消息的不同方式。 3.4.12在指定的Java线程中执行行为 正如3.4节中提到的,行为调度安排以一种没有优先级的方式实现,比如行为的action()方 法从来不会被打断而让其它行为运行。只有当当前运行行为的action()方法返回,控制权才 会交给下一个行为。正如曾经讨论过的,这个方法在功能和看测量性方面有几个优点。然而, 当一个行为需要执行一些暂停操作时,它实际上会暂停整个agent而不仅仅是它自己。解决 这个问题一个可行的方法当然是使用一般Java线程。但是,JADE通过threaded行为提供了 一个清除方法, 如:在指定线程执行行为。 无论什么样的JADE Behaviour(复合的或简单的)都可以作为threaded行为,用 edBehaviourFactory类来执行。这个类提供wrap()方法真正把普通 的JADE Behaviour封装成ThreadedBehaviour。通过addBehaviour()方法把ThreadedBehaviour 添加到agent,通常导致在指定的线程里执行原始的Behaviour。应该注意:开发者只处理了 ThreadedBehaviourFactory类,而ThreadedBehaviour类是私有的不能访问。 下面的代码样本说明了如何在指定的线程里,执行JADE行为。 在复合行为里,threaded行为可以和普通行为合并。例如,SequentialBehaviour可以有2个 children作为普通行为执行,第三个child在指定线程执行。尤其是ParallelBehaviour类可以 将一组行为分配到一个单一的指定线程。 当处理threaded行为时,要考虑几个重点: Agent类的removeBehaviour()方法对threaded行为没有作用。Threaded行为通过用 ThreadedBehaviourFactory类的getThread()方法恢复它的Thread对象来移除,并调用它 的interrupt()方法。 当一个agent死去,移动或挂起的时候,必须用上面描述的技术,显式地终止它的活动 threaded行为。 当一个threaded行为访问一些其它threaded或non-threaded行为经常访问的agent资源 时,必须注意同步问题。 3.5交互协议 FIPA规定了一系列标准交互协议,它们可以作为标准模板建立agent会话。对agents间的每 一次会话来说,JADE区分Initiator角色(发起会话的agent)和Responder角色(参与会话 的agent)。JADE为会话中的这两个角色提供备好的行为类,遵照大部分FIPA交互协议。 这些类可以在包中找到,如本节中所描述的。它们用同样的API提供一系列 callback方法管理协议的状态。 一旦发起者到达交互协议的任何一个最终状态,所有发起者行为终止,并从agent任务队列 中除去。为了无需重建新的对象就可以重用这些行为的Java对象,所有的发起者包括一些 带有恰当参数的reset方法。而且,所有发起者行为,但不包括FipaRequestInitiatorBehaviour, 是1:N的关系,比如同一时间可以管理若干参与者。 所有的响应者是循环的,一旦它们到达交互协议的任何一个最终状态,就重新排序。注意: 这个特征允许程序员限制agent并向执行的响应者行为的最大数量。例如,下面的代码确保 最多有两个合同网协议任务同时执行。 这些类的完整的参考可以在JADE HTML文档和类参考文件中找到。 从JADE2.4开始添加了一对新的类,AchieveREInitiator/Responder,这为所有的 FIPA-Request-like交互协议,包括FIPA-Request本身,FIPA-query,FIPA-Request-When, FIPA-recruiting,FIPA-brokering,…提供了有效的执行。作者的目的是维持这对类,并尽快 轻视其它类。 从JADE2.5开始应用ContractNetInitiator/Responder,以同样的方式提供API和功能,代替 被轻视的旧的FipaContractNetInitiator/ResponderBehaviour. 从JADE2.6开始,提供一个AchiveREInitiator/Responder更简单(对程序员可用的方法更少) 和更快(执行更少的代码)应用,命名为SimpleAchiveREInitiator/Responder。 从JADE2.6类SubscriptionInitiator/Responder对FIPA-Subscribe协议开始生效。注意:因为 这个协议的定义仍然在FIPA的讨论中,这两个类和它们的APIs在将来JADE的版本中可能 会改变。 从JADE3.1ProposeInitiator/Responder提供FIPA-propose协议的应用,同 AchieveREInitiator/Responder类似。 3.5.1 AchieveRE(Achieve Rational Effect) 从功能角度来说,在FIPA ACL里的消息代表通信行为,只是一个agent可以实现的行为之 一。FIPA标准为每一个行为指明了可行性前提(agent执行行为之前必须满足的条件)和推 理结果,i.e.行为的期望结果,或换句话说,发送消息的理由。该标准还指明行为执行了(i.e. 已经发送消息),发送者agent不能确信有把握得到推理结果;例如,假定它是自治的,接 收者agent可能简单地决定忽略收到的消息。在大部分应用程序中并我希望那样,因为这会 产生预料之外的不确定性。因为这个原因,代替发送单一消息,交互协议应该由发送者agent 初始化,允许核实是否接收到期望的推理结果。 FIPA已经规定了一些这类交互协议,如FIPA-Request,FIPA-query,FIPA-Request-When, FIPA-recruiting,FIPA-brokering,这些协议允许发起者核实单一通信行为的期望推理结果是 否已经到达。因为它们共享同样的结构,JADE提供一对AchieveREInitiator/Responder类, 它们是所有这些交互协议的一个单独的同类的应用。 图5说明了这些交互协议的结构。发起者发送一条消息(一般的它执行一个通信行为,如白 表框中所示)。然后响应者可以发送一个not-understood或refuse作为回复,达到通信行为的 推理结果,也可以发送一个agree消息达成协议执行(在未来有可能)该通信行为,如第一 排有阴影的表框中所示。响应者执行行为,最后必须用inform返回行为结果(行为最终完 成)或者如果任何地位出错则用failure返回。注意:我们已经扩展协议有选择的发送agree 消息。事实上,大多数情况下,执行行为需要很短的时间所以发送agree消息是一个无用的 且无效的耗费;在这种情况下,对执行通信行为的agree就包含在协议里接下来的消息中的 接收中。 3.5.1.1AchieveREInitiator 这个类的一个实例可以很容易建立起来,通过传递作为它的构造器的参数的消息,初始化协 议。重要的是,这个消息具有正确的ACLMessage的protocol槽的值,它是由 包中的ctionProtocols接口中的常量定义的。 注意:ACLMessage对象在这个类的构造器建立的时候可能还不完善;调用返回方法 prepareRequests可以重载,以返回完整的ACLMessage或者更精确地(因为这个发起者允许 管理1:N的对话)发送ACLMessage对象的向量。 这个类可以很容易的通过重载它的一个(或全部)handle…调用返回方法扩展,这提供了管 理协议全部状态的方法。例如,当接收到refuse消息的时候调用handleRefuse方法。 熟练的程序员可能发现有用的,代替扩展这个类和重写一些它的方法,注册程序特定的 Behaviour作为协议状态的控制器,包括,例如另一个AchieveREInitiator行为在同意执行通 信行为前请求一个密码。方法registerHandle…允许这样做。重载方法的混合和注册行为可 能通常是最好的解决方法。 有必要澄清下面三种控制器间的区别: handleOutOfSequence管理所有意外接收到的消息,该消息具有正确的conversation-id 或者in-reply-to值 handleAllResonses管理所有收到的第一个相应(例如,not-understood,refuse,agree), 在收到每一个单一响应调用handleNotUnderstood/Refuse/Agree后调用 handleAllResonses。在1:N的会话情况下,这个方法的重载可能其它方法的重载更有 用,因为这个方法允许在一个单一的调用中管理所有的消息。 handleAllResultNotifications管理所有的接收到的第二响应(例如failure,inform),每个 单一响应收到调用handleFailure/Inform后调用handleAllResultNotifications。在1:N的 会话情况下,这个方法的重载可能比其它方法的重载更有用,因为这个方法允许在单一 调用中管理所有的消息。 一个变量集合(它们不是常量)很有用(…_KEY),它们提供从这个行为数据库中检索下 列消息的线索: getDataStore().get(ALL_RESPONSES_KEY)返回第一响应(例如,not-understood,refuse, agree)组成的ACLMessage对象的向量 getDataStore()get(ALL_RESULT_NOTIFICATIONS_KEY)返回第二响应(如,failure, inform)组成ACLMessage对象的向量 getDataStore()get(REQUEST_KEY)返回在类的构造器中传递的ACLMessage getDataStore()get(ALL_REQUESTS_KEY)返回由prepareRequests方法返回的 ACLMessage对象组成的向量。记住:如果一个行为作为管理者注册为PrepareRequsets 状态,这个行为有责任将正确的由这个发起者发送的ACLMessage对象(合适的键值约 束的)向量放入数据库中。 这个应用管理时间间隔的期满,表示为发送的ACLMessage对象的reply-by槽的值。在1: N的会话中,在发送的ACLMessage对象的reply-by槽所有值中估计并使用最小值。注意: 就像FIPA定义的一样,这个时间间隔涉及到接收到第一个响应的时候。如果程序需要限制 接收到最后一个inform消息的时间间隔,它们必须通过程序特定的本体将这个限制添加到 消息内容中。 3.5.1.2 SimpleAchieveREInitiator 这个类是发起者角色的一个简单应用。和AchieveREInitiator主要的区别是协议的版本不允 许程序员注册程序特定的Behaviour作为协议状态的管理器。这个简单的发起者是1:1;如 果程序员设置多于一个的接收者给ACLMessage传入构造器(或由调用返回方法 prepareRequest方法返回),消息将只发送给第一个接收者。这个应用也管理时间间隔期限。 该类可以像前面介绍的AchieveREInitiator一样通过重载handle…方法很容易地扩展。 3.5.1.3AchieveREResponder 这个类是响应者角色的一个应用。它对传递正确的消息模板作为它的构造器的参数非常重 要,事实上它用于选择应该处理哪一个接收到消息。createMessageTemplate方法可以用于为 给定的交互协议创建消息模板,但在有些情况下,多的选择性模板可能有用,例如为每一个 可能的发送者agent创建这个类的一个实例。 这个类可以通过重载一个(或全部)它的prepare很简单地扩展...该方法提供协议状态的管 理接口,尤其是准备响应消息。当发起者的消息被接收和第一个响应(如,agree)必须返 回的时候调用该方法prepareResponse;当推理结果完成(例如该行为必须在FIPA-Request 协议下执行)且最后的响应消息(如inform(done))被返回的时候调用方法 prepareResultNotification。注意:返回正确的消息和设置ACLMessage所有需要的槽;一般 地推荐通过ACLMessage类的createReply()方法创建回复消息。 熟练的程序员可以发现有用的,代替扩展这个类和重写一些它的方法,注册程序特定的 Behaviour作为协议状态的管理者。方法registerPrepare…允许这样做。重载方法的混合和注 册行为可能通常是最好的解决方法。 一个变量集合(它们不是常量)是有用的(…_KEY),它们提供从这个Behaviour数据库检 索以下信息的键值: getDataStore().get(REQUEST_KEY)返回发起者接收到的ACLMessage对象 getDataStore().get(RESPONSE_KEY)返回发送给发起者的第一个ACLMessage对象 getDataStore().get(RESULT_NOTIFICATION_KEY)返回发送给发起者的第二个 ACLMessage对象 记住:如果Behaviour注册为Prepare…状态的管理器,这个行为有责任将由这个响应者发送 的正确的ACLMessage对象放入数据库(带有正确的键值)中。 3.5.1.4SimpleAchieveREResponder 这个类是AchiveREResponder的一个简单应用。主要区别是这个版本不允许程序员注册 Behaviour作为协议状态的管理器。这种情况下要记住:传递正确的MessageTemplate作为 构造器的参数。这个类可以通过重载prepare…方法很容易地扩展,如前面介绍的 AchieveREResponder。 3.5.1.5用这两个一般类执行一个特殊的FIPA协议的例子 上面介绍的两个类可以很容易地用于执行FIPA定义的交互协议。 下面的例子说明了如何添加一个FIPA-Request发起者行为: 下面的例子说明了如何添加一个FIPA-Request响应者行为: 3.5.2FIPA-Contract-Net(FIPA合同网协议) 这个交互协议允许发起者向一系列响应者发送Proposal请求,评估它们的提议,接受首选项 (或者甚至拒绝所有的建议)。交互协议进一步的描述在FIPA规范当中,下面的图是为程 序员的一个简化说明。 发起者通过发送一个CFP消息从其它agents请求proposals,说明将被执行的行为,如果根 据执行条件需要的话。然后,响应者发送一个PROPOSE消息作为回复,包括他们为行为设 置的前提条件,例如价格或时间。响应者可能发送一个REFUSE消息来拒绝该建议,或最 终,一个NOT-UNDERSTOOD沟通通讯问题。发起者可以评估所有的接收到的提议,做出 选择,选择哪一个agent提议将被接收,哪一个被拒绝。一旦提议被接受的响应者(例如, 那些已经接收到一个ACCEPT-PROPOSAL消息)完成它们的任务,最后,它们以行为结果 的INFORM作为响应(最后只是一个完成的行为)或者如果任何地方出错,则回复一个 FAILURE。 3.5.2.1ContractNetInitiator 这个行为实现了fipa-contract-net交互协议,它是从发起该行为的agent,也就是发送cfp(请 求提议)消息的agent角度来实现的。 这个行为也考虑管理等待回答的时间间隔问题。时间间隔是从构造器中传递的ACLMessage 的reply-by字段得到的;如果该字段没有设置,那么就使用无限时间间隔。当然,时间间隔 到期后迟到的回答也不会被销毁而是保存在agent的私人引入ACLMessages队列中。 该协议的执行提供了一系列复查方法来管理协议的每个状态,和收到某一类型消息(基于它 的通信行为)的时候调用哪一个方法。它也提供两个集合来管理复查方法,在收到所有第一 层回复(i.e. not-understood,refuse,propose)和第二层回复(i.e. failure,inform)后,分别 调用handleAllResponses和handleAllResultNotifications。 作为复查方法的替代者,该应用还提供了注册一般Behaviours作为每个相应管理方法的管 理器和重载每个管理方法的可能。在这种情况下,行为的数据库必须作为共享的存储器用于 交换消息。 这个应用还提供一系列常量,由后缀KEY定义的,代表访问数据库和检索所有发送和接收 消息的键值。 3.5.2.2ContractNetResponder 这个行为类从一个请求提议(cfp)消息的响应者角度实现了fipa-contract-net交互协议。重 要的是要传递正确的消息模板作为构造器的参数,事实上,它用来选择应该处理哪一个接收 到的ACLMessage。方法createMessageTemplate可以用来为给定的交互协议创建消息模板, 但是有些情况下,多个选择性模板更有用,例如为每一个可能的发送者agent创建一个这个 类的实例。 这个类可以通过重载它的一个(或全部)prepare…很容易的扩展,这些方法提供了管理协议 状态的接口,尤其是准备响应消息。当接收到发起者的消息的时候调用方法prepareResponse, 而且必须返回第一响应(e.g. propose);当收到ACCEPT_PROPOSAL消息(al被 发起者接受)的时候,调用方法prepareResultNotification,而且必须返回最后的响应消息 ((done))。注意:返回正确的消息,设置所有ACLMessage需要的槽;一般地, 强烈推荐通过ACLMessage类的createReply()方法创建返回消息。 熟练的程序员可以找到有用的,代替扩展这个类和重载它的一些方法,注册程序特定的 Behaviour作为协议状态的管理器。方法registerPrepare…允许这样做。重载方法的混合和注 册行为可能通常是最好的解决办法。 一系列变量(它们不是常量)是有用的(…_KEY),它们提供从Behaviour数据库检索下列 信息的键值。 3.5.3 FIPA-Propose 这个交互协议允许发起者发送一条propose消息给参与者,指明如果参与者同意,它将要执 行的一些行为。参与者用接受或反对该propose作为回复,以accept-proposal或reject-proposal 通信行为通讯。以accept-proposal行为完成这个IP将由该proposed行为的发起者的执行将 遵循,然后返回响应状况。 3.5.3.1 ProposeInitiator 这个行为从发起该协议的agent也就是发送propose消息的agent角度实现了fipa-propose交 互协议。 Javadoc文档提供了该API的完整的描述,类似于AchieveREInitiator类的API。 该行为还负责管理等待回复的时间间隔。改时间间隔是从构造器里传递的ACLMessage里的 reply-by字段得到的;如果没有设置该字段,则是无限时间间隔。当然,时间间隔到期后到 达的消息不会被销毁,而是保存在agent的引入ACLMessage的私人队列中。 该协议的这个应用提供了一系列复查方法来管理协议的每个状态,当接收到某一类型消息 (基于它的通信原语)的时候,调用哪一个方法。它也提供一个集合来管理复查方法,所有 层的回复(-understood,accept-proposal,reject-proposal)收到后,调用 handleAllResponses。 作为复查方法的替代者,该应用提供了注册一般Behaviour作为每个管理方法相应的管理器 和重载每个管理方法的可能。在这种情况下,行为的数据库必须用作共享存储器来交换消息。 该应用提供了一系列常量,由后缀KEY定义的,代表了访问这个数据库和检索所有发送和 接收到的消息的键值。 3.5.3.2 ProposeResponder 这个行为类从propose消息的响应者的角度实现了fipa-propose交互协议。重要的是要传递 正确的消息模板作为构造器的参数,事实上,它用来选择应该处理哪一个接收到的 ACLMessage。方法createMessageTemplate可以用来为给定的交互协议创建消息模板,但是 更多选择性的模板可能在某些情况下有用的,例如,每一个可能发送消息的agent有一个这 个类的实例。 这个类可以通过重载它的prepareResponse方法进行扩展,这提供了准备响应消息的接口。 当发起者的消息被接收的时候,调用该方法prepareResponse,并且必须发送回该响应 (e)。注意:回复正确的消息和设置所有ACLMessage需要的槽;一般强烈推荐 用ACLMessage类的方法createReply()创建回复消息。 熟练的程序员可以找到有用的,代替扩展这个类和重载一些它的方法,注册程序特定的 Behaviour作为协议状态的管理器。该方法registerPrepareResponse允许那样做。 3.5.4 FIPA-Subscribe 这个交互协议[FIPA00035]允许发起者发送subscribe消息给参与者,表明它想得到的订单。 参与者处理该subscribe消息,用接受或拒绝订单回复该请求。如果参与者拒绝了该请求, 它用refuse通信,相反的,如果参与者接受了,则选择agree进行通信。 如果参与者同意了一份订单,它用inform-result传达所有与订单条件匹配的内容,i.e.一个 inform通信原语和一个结果谓词作为内容。参与者继续发送inform-results直到发起者通过 发送cancel消息表示取消,或者参与者觉察到失败,以一个failure消息传达。 这个交互协议的表示在下图中给出,它是基于UML1.x的,直接从FIPA规范[FIPA00035] 中复制过来的。 3.5.4.1 Subscription Initiator 这个行为从发起该协议的agent角度实现FIPA-Subscribe交互协议,该agent发送subscription 消息,并当每次subscriptions条件为true时,接收notifications。响应者可以发送 not-understood,refuse或agree消息作为回复。 每次subscription消息内指明的条件变为true时,响应者发送一条“notification”消息给发 起者。如果在subscription消息的时间间隔到之前收到一个回复或notification,或者所有的 响应者回复refuse或not_understood消息,则发起者行为终止。否则,行为将永远执行下去。 该交互协议的应用提供了一系列复查方法来管理协议的每一个状态;当收到某一类型消息 (基于它的通信原语)的时候,调用这些方法。默认的应用不包含任何操作;功能通过重载 相关方法,与特定的状态相关。 handleAgree(ACLMessage agree) 每次接收到agree消息的时候调用这个方法,根据协议规则不能out-of-sequence。 handleRefuse(ACLMessage refuse) 每次接收到一个refuse消息的时候调用这个方法,根据协议规则不能out-of-sequence。 handleInform(ACLMessage inform) 每次接收到inform消息的时候调用该方法,根据协议规则不能out-of-sequence。 handleAllResponses(Vector responses)当收集到所有的响应或者时间间隔到期的时候调 用该方法。响应消息包括agree,not-understood,refuse和failure,根据协议规则不能 out-of-sequence。 cancel(AID receiver,boolean ignoreResponse) 取消对接收者agent的订单。这个方法检索发送给接收者的subscription消息,并用 conversationID和设置的所有其它协议字段发送一条合适的CANCEL消息。这个 CANCEL消息的内容槽用fillCancelContent()方法填充。 Javadoc文档提供该API的完整描述。 3.5.4.2 Subscription Responder 这个行为类从subscription消息的响应者角度实现了FIPA-Subscribe交互协议。重要的是传 递正确的消息模板给它的构造器,因为它要用来选择将被处理的ACLMessage。响应者接收 到一个subscription消息;这个消息包含为该订单程序特定的条件。一旦订单请求被检测, 响应者必须回复,发送一个not-understood,一个refuse或者一个agree消息来传达订单状态。 每次订单条件变为true,响应者则发送一个“notification”消息给发起者。 3.5.4.2.1Subscription 这个内部类代表一个subscription。当一个notification被发送给一个subscribed agent的时候, 该notification消息不应该直接发送给该subscribed agent,而应该用该agent的notify()方法传 递给代表该agent的subscription的Subscription对象。应该调用这个方法来代替直接使用该 Agent类的send()方法,因为它适当地自动管理先后顺序和协议字段。 3.5.4.2.2 Subscription Manager 一个Subscription Responder只处理subscription会话中消息顺序的执行和控制,而且它代表 subscription的注册/注销和对Subscription Manager对象的notifications的创建,该Subscription Manager对象使用iptionManager接口。 当一个新的subscription消息到达时,该Subscription Responder唤醒它的Subscription Manager 的register()方法,当接收到一条cancel消息的时候,则调用deregister()方法。希望Subscription Manager程序执行register()方法和deregister()方法。 使用了该交互协议的一个例子,见Directory Facilitator Agent()或 JMSPubSub Agent附件。 3.5.5交互协议的一般状态 包包含一些交互协议的一般状态的应用,这对管理器注册可能有用。 3.5.5.1 HandlerSelector类 这个包的抽象类提供了管理器的一般选择器的应用,这里的管理器是一个 our。 该类的构造器要求传递三个参数:agent参数,检索选择变量的数据库参数,和从数据库检 索选择变量的访问键值。 这个选择变量稍后将作为参数传给getSelectionKey,该方法必须返回键值为在注册的管理器 间进行选择。事实上,每个管理器必须通过方法registerHandler注册一个键值。 这个类的用法的有用的例子,例如,为每个行为名字(es.由行为“registerBehaviour”管理 的行为“register”,被其它修改的行为,每个行为都如此)选择不同的管理器。这个类足够 一般,允许大量的选择系统,例如基于消息发送者,内容语言,本体,…程序员只需要扩展 该类和重载它的方法getSelectionKey。 3.5.5.2 MsgReceiver类 这是个一般应用,它等待和给定模板匹配的消息的到达直到给定的时间间隔到期。参考 javadoc中它的用法的文档。 3.6 Application-defined content languages and ontologies 程序特定的本体描述agents用于创建消息内容的元素,e.g.程序特定的谓词和行为。从 JADE2.5发布包t(和它的子包),允许创建程序特定的本体,并作为内容语言独 立地使用它们:应用该本体的代码和发送与接收消息的代码都不依赖于该内容语言。 这个包和它的用法在JADE文档发布的指南中有更深入的描述。 3.7 Support for Agent Mobility 使用JADE,程序开发者可以建立移动agents,这些agents能够跨越多网络主机进行迁移或 者自我复制。在这个JADE版本中,只支持intra-platform的移动,它是一个JADE移动agent 可以跨越不同agent容器导航,但是它局限于一个单一的JADE平台。 移动和克隆被认为是agent生命周期的一个状态转换。就像其它生命周期操作一样,agent 移动或克隆可以由agent自己或AMS发起。Agent类提供了相应的API,但是可以通过FIPA ACL像平常一样访问AMS agent。 移动agents需要location aware(定位意识),以便于决定何时移动,移动到何地。因此,JADE 提供了专属本体,命名为jade-mobility-ontology,管理必要的概念和行为。 这个本体包含在ty包中。 3.7.1 agent 移动的JADE API Agent类的两个公共方法doMove()和doClone()允许JADE agent迁移到其它地方或者以不同 的名字产生一个自身的远程副本。方法doMove()采用on作为它的单一参数, 这代表迁移agent想要到达的目的地。方法doClone()也采用on作为参数,但 是还添加了一个String,包含新agent的名字,作为创建的当前agent的副本的名字。 看文档,可以发现on是一个抽象接口,所以不允许应用agents创建它们自己 的位置。因而,它们必须向AMS请求有效的位置列表并选择一个。而一个JADE agent也可 以请求AMS告诉另一个agent存在的位置。 移动一个agent包括通过网络渠道,发送它的代码和状态,使用用户定义移动agents必须管 理顺序化和非顺序化过程。各种资源中,一些被移动agent使用的资源将被单独移动,而其 它将在移动之前断开连接,到达目的地(这是在Java Serialization API中用到的暂时的和非 暂时的字段间的相同目的地)后重新连接。JADE使一对匹配方法在Agent类中对资源管理 有效。 对agent移动来说,当移动操作已成功完成时,在启动位置调用beforeMove()方法,所以在 目的地容器移动的agent实例将被激活,而原始agent实例将被停止。因此,这个方法是right 占位符释放被原始agent实例占用的任何本地资源(e.g.关闭任何打开的文件和GUI)。的确, 如果这些资源预先被关闭,移动则会失败,将要求重新打开它们。然而,作为及时结果,必 须由agent传输到新位置的任何消息都必须在调用方法doMove()之前设置。例如在 beforeMove()方法中设置agent属性对移动实例没有影响。一旦该agent到达,且它的标识适 当,则在目的地位置调用方法afterMove()(但是行为调度程序还没有重新启动)。 对agent克隆来说,JADE支持相应的方法对,beforeClone()和afterClone()方法,调用方法 同上面的beforeMove()和afterMove()。上面这四个方法都是Agent类的protected方法,定义 为空占位符。用户定义的移动agents如果需要,将重写这个四个方法。 3.7.2 JADE Mobility Ontology jade-mobility-ontology包含支持agent移动的需要的所有概念和行为。JADE提供类 tyOntology作为Singleton,并通过getInstance()方法,提供单一的、 共享的JADE移动本体实例的访问。 从JADEManagementOntology扩展的本体包括五个概念和两个行为,ty包 的合适的类和每个概念或行为相关。下面的表格展示了所有的概念/行为和它们的说明。 Mobile-agent-description:描述一个去向某个地方的agent。它由MobileAgentDescription 表示。 mobile-agent-profile:描述移动agent需要的计算环境。它由MobileAgentProfile类表示。 mobile-agent-system:描述移动agent使用的运行系统。它由MobileAgentSystem类表示。 mobile-agent-language:描述移动agent使用的编程语言。它由MobileAgentLaguage类 表示。 mobile-agent-os:描述移动agnet需要的操作系统。它由MobileAgentOS表示。 move-agent:agent从一个地方移动到另一个地方的行为。它由MoveAction类表示。 这个行为有一个单一的、未命名的mobile-agent-description类型的槽。它的参数是必须的 clone-agent:该行为实现agent的复制,该副本可以在另一个地方运行。它由CloneAction 类表示。 这个行为有两个未命名的槽:第一个是mobile-agent-description类型的,第二个是String类 型的。两个参数都是必须的。 注意:这个本体在任何FIPA规范中都没有相应的部分。JADE团队的目的是随着一套FIPA 规范的更新尽快的更新本体。 3.7.3 Accessing the AMS for agent mobility JADE AMS有一些扩展支持agent移动,并且能够执行jade-mobility-ontology中提到的两个 行为。每个移动相关的行为都可以通过FIPA-request协议请求,jade-mobility-ontology作为 ontology值,FIPA-SL0作为language值。 move-agent行为将mobile-agent-description作为参数。这个行为将由mobile-agent-description 的name和address槽定义的agent移动到description槽表示的位置。 例如,如果一个agent想移动agent Peter到位置Front-End,它必须发送下列ACL request消 息给AMS: 上面的消息可以用JADE嗅探器捕获,用MobileAgent例子和支持移动和克隆agents的RMA。 使用JADE本体支持,一个agent可以很容易地给它的能力添加移动,不需要手动配置ACL 消息。 首先,该agent必须创建一个新的MoveAction对象,用恰当的MobileAgentDescription对象 填充它的参数,按次序填充该agent移动的名字和地址(它自身的或者另一个移动agent) 以及目的地Location对象。然后,单独调用tentManager().fillContent(.., ..)方法, 可以将MoveAction Java对象转换成一个String,并将它写入恰当的request ACL消息的 content槽。 clone-agent行为以同样的方式工作,但是另外还有一个String参数,表示从克隆过程中得到 的新agent的名字。 AMS也支持四个相关行为的移动,定义在JADEManagementOntology中。 下面对它们进行介绍: where-is-agent行为有一个单一的AID参数,表示定位agent的标识。这个行为有一个 结果,即agent的位置,放在inform ACL消息的content槽中,它可以成果关闭该协议。 例如,request消息请求agent Peter所在的位置,代码如下: 结果Location将包含在inform消息中,如下所示: query-platform-locations行为没有参数,但是它的结果是一系列在当前JADE平台有效 的Location对象。这个行为的消息很简单: 如果当前平台有三个容器,AMS将发送回下面的inform消息: Location类运用on接口,所以它可以被传给()和e() 方法。JADE移动agent的典型地行为方式就是向AMS请求地址(完整的列表或通过一个或 多个where-is-agent行为);然后,agent就能够决定是否,何地和何时移动。 3.8从外部Java程序使用JADE 从JADE2.3开始,使用in-process接口,它允许外部Java程序将JADE作为一种库使用,从 程序自身启动JADE运行时环境。 一个单独的JADE运行时实例可以通过静态方法ce()获得,它提供两 个方法来创建一个JADE主容器或者一个JADE远程容器(i.e.参与一个已存在的主容器的 容器,以发布的agent平台方式形成);两个方法都要求传递一个e对象参数 来维持JADE运行时环境启动需要的配置选项(e.g.主容器的主机名和端口号)。 运行时的这两个方法都返回一个封装对象,属于r包,封装了agent容器的高水 平的功能,例如安装和卸载MTPs(Message Transport Protocol),销毁容器(只有容器销毁, 而外部程序仍然存在),当然,还有创建新的agents。这个容器封装的createNewAgent方法 也返回一个封装对象,封装了一些agnet的功能,但是仍然保持了agents的自治。尤其是, 该程序可以控制Agent的生命周期,但是不能获得Agent对象的直接访问,而且,作为直接 结果,它不能执行访问该对象的方法。注意:agent创建后,它仍然需要通过start()方法启 动。 下面的代码列举了一个非常简单的从外部程序启动agent的方式(也涉及到JADE的examples 中的inprocess目录,它包含这个封装和in-process接口用法的例子)。 注意:这个机制允许若干个不同的JADE平台配置,如在同一个JVM上由多个容器组成的 完整的in-process平台,部分是in-process(i.e.由外部Java程序启动的容器)部分是 out-of-process(i.e.从命令行启动的容器)的平台。 4. Agent系统例子 我们将用一个agent系统的例子来解释如何在JADE框架中有效地使用这些功能。我们将展 示在不同子行为中组织单个agent行为的可能性,和agents之间的信息交互是如何发生的。 在例子中,agent系统由两个agents组成,它们通过FIPA request协议通信。 这部分仍然在做,请参考在src/examples目录下的JADE examples。也可以参考src/examples 目录下的README文件得到一些每个例子程序的解释。
发布评论