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文件得到一些每个例子程序的解释。