ExtensibleMessaging and Presence Protocol,简单的来讲,它就是一个发送接收处理消息的协议,但是这个协议发送的消息,既不是二进制的东东也不是字符串,而是XML。正是因为使用了XML作为消息传递的中介,Extensible 才谈的上,不是么?
InstantMessenger,及时通信软件,就是大家使用的QQ、MSN Messenger和Gtalk等等。其中Gtalk 就是基于XMPP 协议的一个实现,其他的则不是。当前IM 几乎作为每个上网者必然使用的工具,在国外的大型企业中有一些企业级的IM应用,但是其商业价值还没完全发挥出来。设想既然XMPP 协议是一个公开的协议,那么每个企业都可以利用它来开发适合本身企业工作,提高自身生产效率的IM;甚至,你还可以在网络游戏中集成这种通信软件,不但让你可以边游戏边聊天,也可以开发出适合游戏本身的IM 应用,比如说一些游戏关键场景提醒功能,团队语音交流等等都可以基于IM来实现。
开源界总是有许多有趣的东东,这三个合起来就是一个完整的XMPP IM 实现。包括服务器端——Openfire,客户端——Spark,XMPP 传输协议的实现——Smack(记住,XMPP是一个协议,协议是需要实现的,Smack起到的就是这样的一个作用)。三者都是基于Java 语言的实现。
Spark 提供了客户端一个基本的实现,并提出了一个很好的插件架构,这对于开发者来说不能不说是一个福音。我强烈建议基于插件方式来实现你新增加的功能,而不是去改它的源代码,这样有利于你项目架构,把原始项目的影响降到最低。
Openfire 是基于XMPP 协议的IM 的服务器端的一个实现,虽然当两个用户连接后,可以通过点对点的方式来发送消息,但是用户还是需要连接到服务器来获取一些连接信息和通信信息的,所以服务器端是必须要实现的。Openfire 也提供了一些基本功能,但真的很基本的!庆幸的是,它也提供插件的扩展,像Spark 一样,同样强烈建议使用插件扩展的方式来增加新的功能,而不是修改人家的源代码。
Smack 是一个XMPP 协议的Java 实现,提供一套可扩展的API,不过有些时候,你还是不得不使用自己定制发送的XML 文件内容的方式来实现自己的功能
下图展示了三者之间的关系:
从图上可以了解到,client 端和server端都可以通过插件的方式来进行扩展,smack是二者传递数据的媒介。
Openfire的通信处理基于Apache MINA框架实现。Apache MINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可靠性的网络应用程序。它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API。
Apache MINA 也称为:
● NIO 框架库
●客户端服务器框架库
●一个网络套接字库
MINA虽然简单但是仍然提供了全功能的网络应用程序框架:
●为不同的传输类型提供了统一的API:
○通过Java NIO提供TCP/IP 和 UDP/IP支持
○通过RXTX提供串口通讯(RS232)
○ In-VM管道通讯
○你能实现你自己的API!
●过滤器作为一个扩展特性; 类似Servlet过滤器
●低级和高级的API:
○低级: 使用字节缓存(ByteBuffers)
○高级: 使用用户定义的消息对象(objects)和编码(codecs)
●高度定制化线程模型:
○单线程
○一个线程池
○一个以上的线程池(也就是SEDA)
●使用Java 5 SSL引擎提供沙盒(Out-of-the-box) SSL • TLS • StartTLS支持
●超载保护和传输流量控制
●利用模拟对象进行单元测试
● JMX管理能力
●通过StreamIoHandler提供基于流的I/O支持
●和知名的容器(例如PicoContainer、Spring)集成
●从Netty平滑的迁移到MINA, Netty是MINA的前辈。
Openfire中常见的类名后缀命名包括Starter、Plugin、Listener、Dispatcher、Handler、Manager、Provider,通常情况下,这些命名类包括如下意义:
系统启动类,如org.jivesoftware.openfire.starter.ServerStarter,调用其start()方法可启动系统应用。
业务的最终处理类。
调度类,其中有很多关键方法,如addListener(),以组合的方式,为类内定义的静态Set<XXListener>实例添加XXListener对象。以便调用dispatchEvent(Stringproperty, EventType eventType, Map<String, Object> params)方法遍历处理Set集中的XXListener对象(通过调用XXListener对象的各实际方法完成实际业务)。
实现Plugin接口的插件类,需实现initializePlugin(PluginManagermanager, File pluginDirectory)方法和destroyPlugin()方法。在其初始化方法中调用Dispatcher实现类的addListener()方法如PropertyEventDispatcher.addListener(this)。
实现面向接口编程方式的接口类,通过反射机制创建具体实现类的对象,反射类名配置在ofproperty表对应的记录propvalue属性中。若没有相关配置,则调用默认实现类,默认实现类类名命名规则为DefaultXXProvider。
实际处理类,以ConnectionHandler为例,在org.jivesoftware.openfire.spi.ConnectionManagerImpl类的startClientSSLListeners(StringlocalIPAddress)方法中,有这样一段代码:
sslSocketAcceptor.bind(newInetSocketAddress(bindInterface, port), newClientConnectionHandler(serverName));其中bind方法的第二个参数是新创建的一个ClientConnectionHandler的实例,而它就是ConnectionHandler的一个子类。
Openfire的系统配置项采用文件结合数据库表的方式配置,也有部分默认配置项通过Java硬编码方式配置(如org.jivesoftware.openfire. ConnectionManager接口类中定义的DEFAULT_PORT、DEFAULT_SSL_PORT、DEFAULT_COMPONENT_PORT等),Openfire中比较重要的配置位置包括:
一、 src/conf目录下的openfire.xml配置文件。该配置文件为系统核心配置文件。在第一次启动Openfire并通过管理控制台完成安装配置后会往该配置文件中填入相应的配置信息。
二、 plugin.xml配置文件。该配置文件为各插件包下的核心配置文件,由它确定插件核心处理类和相应页面插件的展现等。配置项及含义详见官方插件开发说明部分。
三、 web.xml和web-custom.xml配置文件。用于配置servlet和用户自定义servlet(插件页面用,放在插件对应目录下)。
四、 ofproperty中的各条记录,该表中包括两个字段name和propvalue,分别代表配置项名和配置项值。
系统启动时调用ServerStarter类中的start()方法,通过反射加载org.jivesoftware.openfire.XMPPServer类文件,创建实例时调用其构造函数,在其构造函数中调用其start()方法实际启动服务应用程序。Start()方法中首先调用verifyDataSource()方法验证并确保数据库可以访问,然后会调用 loadModules();initModules();startModules();方法来对Module接口的实现类的各子类进行操作,依次完成模块的加载、初始化和启动操作。loadModules()方法中会调用loadModule(String module)方法通过反射加载各模块类,参数字符串module为对应的模块核心处理类的类名,如AdHocCommandHandler。现以AdHocCommandHandler为例对接下来的处理流程进行说明。通过loadModule创建AdHocCommandHandler类实例时调用其构造函数,在构造函数中初始化了其私有AdHocCommandManager对象。在initModules()时调用AdHocCommandHandler实例的initialize(XMPPServer server)方法对其私有属性对象进行初始化。然后调用start()方法,调用addDefaultCommands方法添加命令并启动命令(通过调用startCommand(AdHocCommand command)方法实现)。
SSL等监听服务的调度在ConnectionManagerImpl类中实现。ConnectionManagerImpl.createClientSSLListeners()方法启动SSL监听
信息处理采用XML节的方式传递信息,消息封装通常采用IQ、Message、Presence。
Openfire的数据库处理采用直接调用JDBC 的方式。核心类为org.jivesoftware.database.DbConnectionManager。数据库的处理与业务处理耦合,没有划分出专门的业务逻辑层。
此类为数据库提供者接口,如需连接mysql、hsqldb等数据库,需首先实现些接口,
通常直接调用XXManager中的实例方法,XXManager中又调用的是对应的接口XXProvider的方法,实际操作在该接口的实现类中实现。实现类是动态绑定的(默认的实现类通常命名规则为DefaultXXProvider),在运行时根据ofproperty表中对应配置项值选择。下面以添加用户组为例进行说明。
首先获得GroupManager的一个实例,在调用其构造函数时调用initProvider()方法,在该方法中获取数据库中配置项的值,若不为空则根据该值通过反射机制获取GroupProvider接口的实现类实例对象;若为空则以DefaultGroupProvider作为GroupProvider接口的实现类并创建实例对象,然后调用GroupProvider.createGroup(String name)方法完成业务操作。
org.jivesoftware.database.DbConnectionManager
连接管理类
org.jivesoftware.util.JiveGlobals
通常用于操作ofproperty表中记录
数据库表
以下是一个说明每个表格的Openfire数据库架构。黄色行表示主键。
· ofGroup
· ofID
· ofUser
· ofRoster
· ofVCard
ofGroup (用户组的数据) |
|||
列名 |
类型 |
长度 |
描述 |
groupName |
VARCHAR |
50 |
组名称(主键) |
description |
VARCHAR |
255 |
组描述 |
ofGroupProp (名称值协会为一组) |
|||
列名 |
类型 |
长度 |
描述 |
groupName |
VARCHAR |
50 |
组名称(主键) |
name |
VARCHAR |
100 |
组属性名称(主键) |
propValue |
VARCHAR |
4000 |
组属性值 |
ofGroupUser (组成员) |
|||
列名 |
类型 |
长度 |
描述 |
groupName |
VARCHAR |
50 |
组名称(主键) |
username |
VARCHAR |
100 |
用户名(主键) |
administrator |
NUMBER |
n/a |
是否为管理员(布尔)(主键) |
ofID (用于唯一ID序列生成) |
|||
列名 |
类型 |
长度 |
描述 |
idType |
NUMBER |
n/a |
证件类型(例如,组,用户名册)(主键) |
id |
NUMBER |
n/a |
下一个可用块编号的(用于数据库独立编号) |
ofOffline (离线邮件存储) |
|||
列名 |
类型 |
长度 |
更改 |
username |
VARCHAR |
32 |
用户名(主键) |
messageID |
NUMBER |
n/a |
存储信息的编号(主键) |
creationDate |
VARCHAR |
15 |
日期信息存储 |
messageSize |
NUMBER |
n/a |
邮件的大小以字节为单位 |
stanza |
TEXT |
n/a |
消息文本 |
ofPresence (离线的存在) |
|||
列名 |
类型 |
长度 |
更改 |
username |
VARCHAR |
64 |
用户名(主键) |
offlinePresence |
TEXT |
n/a |
存在的信息设置为用户注销 |
offlineDate |
CHAR |
15 |
信息存储日期 |
ofPrivate (私人数据存储) |
|||
列名 |
类型 |
长度 |
描述 |
username |
VARCHAR |
32 |
用户名(主键) |
name |
VARCHAR |
100 |
姓名私营项(主键) |
namespace |
VARCHAR |
200 |
名字空间私营项(主键) |
privateData |
TEXT |
n/a |
价值的私人数据 |
ofUser (用户数据) |
|||
列名 |
类型 |
长度 |
描述 |
username |
VARCHAR |
32 |
用户名(主键) |
plainPassword |
VARCHAR |
32 |
纯文字密码数据 |
encryptedPassword |
VARCHAR |
255 |
加密的密码数据(默认) |
name |
VARCHAR |
100 |
名字 |
|
VARCHAR |
100 |
电邮地址 |
creationDate |
VARCHAR |
15 |
创建日期 |
modificationDate |
VARCHAR |
15 |
最后更新日期 |
ofUserProp (名称值协会针对用户) |
|||
列名 |
类型 |
长度 |
描述 |
username |
VARCHAR |
32 |
用户名(主键) |
name |
VARCHAR |
100 |
用户属性名称(主键) |
propValue |
VARCHAR |
4000 |
用户属性值 |
ofUserFlag (用户类型标识(如残疾人)) |
|||
列名 |
类型 |
长度 |
描述 |
username |
VARCHAR |
64 |
用户名(主键) |
name |
VARCHAR |
100 |
用户属性名称(主键) |
startTime |
CHAR |
15 |
国旗的时候,开始被有效(无效的'现在' ) |
endTime |
CHAR |
15 |
当时国旗是结束有效(无效的'永远' ) |
ofRoster (好友列表) |
|||
列名 |
类型 |
长度 |
描述 |
rosterID |
NUMBER |
n/a |
编号名册(主键) |
username |
VARCHAR |
32 |
用户名 |
jid |
TEXT |
n/a |
地址名册入境 |
sub |
NUMBER |
n/a |
认购地位入境 |
ask |
NUMBER |
n/a |
卖出地位入境 |
recv |
NUMBER |
n/a |
检举表明进入名册收到请求 |
nick |
VARCHAR |
255 |
昵称分配给这个名册入境 |
ofRosterGroups (组的好友名单中的条目) |
|||
列名 |
类型 |
长度 |
描述 |
rosterID |
NUMBER |
n/a |
名册编号(主键) |
rank |
NUMBER |
n/a |
立场项(主键) |
groupName |
VARCHAR |
255 |
用户定义的名称,这个名册组 |
ofPrivacyList (用户隐私清单) |
|||
列名 |
类型 |
长度 |
描述 |
username |
VARCHAR |
32 |
用户名(主键) |
name |
VARCHAR |
100 |
姓名保密清单(主键) |
isDefault |
NUMBER |
n/a |
检举指出,如果这是默认隐私的用户名单 |
list |
TEXT |
n/a |
XML表示的隐私清单 |
ofVCard (电子名片的联系信息) |
|||
列名 |
类型 |
长度 |
描述 |
username |
VARCHAR |
32 |
用户名(主键) |
vcard |
TEXT |
n/a |
价值的vCard入境 |
ofVersion (包含产品版本信息) |
|||
列名 |
类型 |
长度 |
描述 |
name |
VARCHAR |
50 |
名称的项目,版本信息正在跟踪的(主键) |
version |
INTEGER |
n/a |
版本号 |
ofProperty (服务器属性) |
|||
列名 |
类型 |
长度 |
描述 |
name |
VARCHAR |
100 |
属性名称(主键) |
propValue |
TEXT |
n/a |
进入值 |
ofExtComponentConf (外部元件配置) |
|||
列名 |
类型 |
长度 |
描述 |
subdomain |
VARCHAR |
255 |
子的外部元件(主键) |
secret |
VARCHAR |
255 |
共享密钥的外部元件 |
permission |
VARCHAR |
10 |
许可,表明如果组件是可以连接到服务器 |
ofRemoteServerConf (远程服务器配置) |
|||
列名 |
类型 |
长度 |
描述 |
xmppDomain |
VARCHAR |
255 |
域的外部元件(主键) |
remotePort |
NUMBER |
n/a |
港口的远程服务器连接到 |
permission |
VARCHAR |
10 |
许可,表明如果远程服务器可以连接到服务器 |
ofSecurityAuditLog (伐木安全事件) |
|||
列名 |
类型 |
长度 |
描述 |
msgID |
NUMBER |
n/a |
编号审计信息(主键) |
username |
VARCHAR |
64 |
使用者谁执行的行动 |
entryStamp |
NUMBER |
n/a |
时间戳当事件发生 |
summary |
VARCHAR |
255 |
总结了发生在事件 |
node |
VARCHAR |
255 |
节点事件发生 |
details |
TEXT |
n/a |
详细的细节,所发生的 |
ofMucService (甲Groupchat服务) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
NUMBER |
n/a |
编号的服务(收录) |
subdomain |
VARCHAR |
255 |
子服务(主键) |
description |
VARCHAR |
255 |
服务说明 |
isHidden |
NUMBER |
n/a |
1 ,如果隐藏的管理界面名单, 0正常 |
ofMucServiceProp (名称值协会的Groupchat服务) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
NUMBER |
n/a |
编号的服务(主键) |
name |
VARCHAR |
100 |
属性名称(主键) |
propValue |
TEXT |
n/a |
属性值 |
ofMucRoom ( Groupchat室内资料) |
|||
列名 |
类型 |
长度 |
描述 |
roomID |
NUMBER |
n/a |
编号的房间(主键) |
creationDate |
VARCHAR |
15 |
创建日期 |
modificationDate |
VARCHAR |
15 |
最后更新日期 |
name |
VARCHAR |
50 |
姓名房间用作公共编号 |
naturalName |
VARCHAR |
255 |
天然名称室 |
description |
VARCHAR |
255 |
客房描述 |
canChangeSubject |
NUMBER |
n/a |
检举指出是否可以改变参与者的主题 |
maxUsers |
NUMBER |
n/a |
马克斯一些房间居住者 |
canChangeSubject |
NUMBER |
n/a |
检举指出是否与会者可以改变的主题或不 |
publicRoom |
NUMBER |
n/a |
检举指示是否室将在目录中列出或不 |
moderated |
NUMBER |
n/a |
检举指示是否室主持或不 |
membersOnly |
NUMBER |
n/a |
检举指出是否房间是会员制或不 |
canInvite |
NUMBER |
n/a |
检举指出是否占用可以邀请其他用户 |
roomPassword |
VARCHAR |
50 |
密码数据加入室 |
canDiscoverJID |
NUMBER |
n/a |
检举指出是否真正JID的居住者是公共或不 |
logEnabled |
NUMBER |
n/a |
检举指出是否房间谈话记录或不 |
subject |
VARCHAR |
100 |
最后为人所知的主题房间 |
rolesToBroadcast |
NUMBER |
n/a |
二元代表的作用,以广播 |
useReservedNick |
NUMBER |
n/a |
检举指出是否用户只能加入室使用其保留昵称 |
canChangeNick |
NUMBER |
n/a |
检举指出是否可以改变其占用的空间昵称 |
canRegister |
NUMBER |
n/a |
检举显示用户是否被允许登记室 |
ofMucRoomProp (名称值协会的Groupchat房间) |
|||
列名 |
类型 |
长度 |
描述 |
roomID |
NUMBER |
n/a |
编号的房间(主键) |
name |
VARCHAR |
100 |
属性名称(主键) |
propValue |
VARCHAR |
4000 |
属性值 |
ofMucAffiliation (归属的空间用户) |
|||
列名 |
类型 |
长度 |
描述 |
roomID |
NUMBER |
n/a |
编号的房间(主键) |
jid |
TEXT |
n/a |
用户JID (主键) |
affiliation |
NUMBER |
n/a |
一些代表所属一级 |
ofMucMember (室成员资料) |
|||
列名 |
类型 |
长度 |
描述 |
roomID |
NUMBER |
n/a |
编号的房间(主键) |
jid |
TEXT |
n/a |
用户JID (主键) |
nickname |
VARCHAR |
255 |
保留昵称的会员 |
ofMucConversationLog (室会话日志) |
|||
列名 |
类型 |
长度 |
描述 |
roomID |
NUMBER |
n/a |
编号的空间 |
sender |
TEXT |
n/a |
JID的用户发送邮件的房间 |
nickname |
VARCHAR |
255 |
昵称使用时由用户发出的信息 |
logTime |
VARCHAR |
15 |
日期的消息时,被送到房间 |
subject |
VARCHAR |
50 |
新的主题改变的信息 |
body |
TEXT |
n/a |
消息正文 |
ofPubsubNode (节点pubsub服务) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
VARCHAR |
100 |
编号托管服务节点(主键) |
nodeID |
VARCHAR |
100 |
编号的节点(主键) |
leaf |
NUMBER |
n/a |
检举表明节点是否是叶或收集节点 |
creationDate |
VARCHAR |
15 |
创建日期 |
modificationDate |
VARCHAR |
15 |
最后更新日期 |
parent |
VARCHAR |
100 |
编号的父节点(如果有的话) |
deliverPayloads |
NUMBER |
n/a |
检举指出是否有效载荷中包含的通知 |
maxPayloadSize |
NUMBER |
n/a |
最大规模的有效载荷的字节 |
persistItems |
NUMBER |
n/a |
检举表明节点是否将持续出版项目 |
maxItems |
NUMBER |
n/a |
最大的项目数量将持续 |
notifyConfigChanges |
NUMBER |
n/a |
检举指出是否发送通知时,该节点的配置发生了变化 |
notifyDelete |
NUMBER |
n/a |
检举指出是否发送通知时,该节点将被删除 |
notifyRetract |
NUMBER |
n/a |
检举指出是否发送通知时,发布的项目将被删除 |
presenceBased |
NUMBER |
n/a |
检举指出是否发送通知只有用户才 |
sendItemSubscribe |
NUMBER |
n/a |
检举指出是否向去年出版项目,以新用户 |
publisherModel |
VARCHAR |
15 |
Publisher中使用的模式的节点 |
subscriptionEnabled |
NUMBER |
n/a |
检举指出是否允许订阅 |
configSubscription |
NUMBER |
n/a |
检举指出是否新的订户必须设定为活跃 |
accessModel |
VARCHAR |
10 |
访问模型所使用的节点 |
payloadType |
VARCHAR |
100 |
类型的有效载荷数据将提供在节点 |
bodyXSLT |
VARCHAR |
100 |
网址的一个XSLT转换有效载荷的格式为一个邮件正文 |
dataformXSLT |
VARCHAR |
100 |
网址的一个XSLT转化的有效载荷格式的数据形式结果 |
creator |
VARCHAR |
1024 |
JID的实体建立了节点 |
description |
VARCHAR |
255 |
说明节点 |
language |
VARCHAR |
255 |
默认语言的节点 |
name |
VARCHAR |
50 |
名称节点 |
replyPolicy |
VARCHAR |
15 |
政策界定业主或出版商是否应得到答复项目 |
associationPolicy |
VARCHAR |
15 |
政策规定谁可以联系叶节点的集合 |
maxLeafNodes |
NUMBER |
n/a |
马克斯一些叶节点,一个节点可能会收集 |
ofPubsubNodeJIDs ( JIDs与节点) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
VARCHAR |
100 |
编号托管服务节点(主键) |
nodeID |
VARCHAR |
100 |
编号的节点(主键) |
jid |
VARCHAR |
1024 |
JID实体(主键) |
associationType |
VARCHAR |
20 |
协会类型的节点 |
ofPubsubNodeGroups (名册集团与节点) |
|||
列名 |
类型 |
长度 |
内容 |
serviceID |
VARCHAR |
100 |
编号托管服务节点 |
nodeID |
VARCHAR |
100 |
编号的节点 |
rosterGroup |
VARCHAR |
100 |
名册组节点所有者可以签署和检索项目 |
ofPubsubAffiliation (节点分支机构) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
VARCHAR |
100 |
编号托管服务节点(主键) |
nodeID |
VARCHAR |
100 |
编号的节点(主键) |
jid |
VARCHAR |
1024 |
JID的子公司(主键) |
affiliation |
VARCHAR |
10 |
所属类别 |
ofPubsubItem (项目发布到节点) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
VARCHAR |
100 |
编号托管服务节点(主键) |
nodeID |
VARCHAR |
100 |
编号的节点(主键) |
id |
VARCHAR |
100 |
编号的出版项目(独特的每个节点)(主键) |
jid |
VARCHAR |
1024 |
JID出版商 |
creationDate |
VARCHAR |
15 |
创建日期 |
payload |
TEXT |
n/a |
XML的有效载荷包括在出版项目 |
ofPubsubSubscription (订阅节点) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
VARCHAR |
100 |
编号托管服务节点(主键) |
nodeID |
VARCHAR |
100 |
编号的节点(主键) |
id |
VARCHAR |
100 |
编号认购(主键) |
jid |
VARCHAR |
1024 |
地址接收通知 |
owner |
VARCHAR |
1024 |
JID的子公司,拥有认购 |
state |
VARCHAR |
15 |
国家认购(工作流程中的) |
deliver |
NUMBER |
n/a |
检举指出是否通知或未启用 |
digest |
NUMBER |
n/a |
检举表明一个实体是否希望收到通知摘要 |
digest_frequency |
NUMBER |
n/a |
最低数目的毫秒之间发出任何两个通知消化 |
expire |
VARCHAR |
15 |
日期在租赁认购将结束或已经结束 |
includeBody |
NUMBER |
n/a |
检举表明一个实体是否希望收到邮件正文除了有效载荷格式 |
showValues |
VARCHAR |
30 |
存在这些国家的实体希望收到通知 |
subscriptionType |
VARCHAR |
10 |
无论是用户订阅的项目或节点(收集节点只) |
subscriptionDepth |
NUMBER |
n/a |
收到通知的儿童一定深度(收集节点只) |
keyword |
VARCHAR |
200 |
关键字活动必须符合 |
ofPubsubDefaultConf (默认配置节点) |
|||
列名 |
类型 |
长度 |
描述 |
serviceID |
VARCHAR |
100 |
编号托管服务节点(主键) |
leaf |
NUMBER |
n/a |
检举指出是否配置属于叶或收集节点(主键) |
deliverPayloads |
NUMBER |
n/a |
检举指出是否有效载荷中包含的通知 |
maxPayloadSize |
NUMBER |
n/a |
最大规模的有效载荷的字节 |
persistItems |
NUMBER |
n/a |
检举表明节点是否将持续出版项目 |
maxItems |
NUMBER |
n/a |
最大的项目数量将持续 |
notifyConfigChanges |
NUMBER |
n/a |
检举指出是否发送通知时,该节点的配置发生了变化 |
notifyDelete |
NUMBER |
n/a |
检举指出是否发送通知时,该节点将被删除 |
notifyRetract |
NUMBER |
n/a |
检举指出是否发送通知时,发布的项目将被删除 |
presenceBased |
NUMBER |
n/a |
检举指出是否发送通知只有用户才 |
sendItemSubscribe |
NUMBER |
n/a |
检举指出是否向去年出版项目,以新用户 |
publisherModel |
VARCHAR |
15 |
Publisher中使用的模式的节点 |
subscriptionEnabled |
NUMBER |
n/a |
检举指出是否允许订阅 |
accessModel |
VARCHAR |
10 |
访问模型所使用的节点 |
language |
VARCHAR |
255 |
默认语言的节点 |
replyPolicy |
VARCHAR |
15 |
政策界定业主或出版商是否应得到答复项目 |
associationPolicy |
VARCHAR |
15 |
政策规定谁可以联系叶节点的集合 |
maxLeafNodes |
NUMBER |
n/a |
马克斯一些叶节点,一个节点可能会收集 |
Openfire采用内置的jetty作web服务器,在启动AdminConsolePlugin插件时调用startup()方法启动jetty服务器,9090为其明文端口,9091为其加密端口。
Openfire没有采用现在很流行的技术架构(SSH),只使用JSP+JavaBean,但是它有自己的系统设计,就连日志都是自己做的,没有使用我们熟悉的log4j。
现有的Openfire管理控制台可采用插件方式进行扩展(详见插件开发说明部分介绍),页面采用Jsp方式实现,页面直接调用业务处理逻辑类(通常命名为XXManager)的实例方法,通常通过request对象封装的方式传递页面展现判定变量,常出现本页跳转。每个插件可定义自己的Servlet类和web.xml及web-custom.xml配置文件。
采用装饰框架方式展现页面,decorator页面有两个,即src/web/decorators目录下的两个页面main.jsp和setup.jsp。采用自定义的admin标签实现,标签库admin.tld放置在src/web/WEB-INF目录下,标签解析类放置在org.jivesoftware.admin包下,有SidebarTag、SubnavTag、SubSidebarTag、TabsTag四个解析类。在调用loadPlugin()方法进行插件加载时,解析插件的plugin.xml配置文件,将获取的相关信息封装在AdminConsole类的generatedModel对象中,后期通过插件解析类提取该对象中的数据并配合sitemesh装饰器进行页面展现。详见“使用dom4j设计Openfire式导航菜单”部分相关介绍。