搞笑聊天记录录

QQ的聊天记录是哪个文件名?_百度知道
QQ的聊天记录是哪个文件名?
来自知道网友专家
MSGEX.db文件 QQ的聊天记录文件存放在QQ目录下以你的QQ号码为文件夹名的‘MsgEx.db’文件中,比如你的QQ号码为:12345,那么聊天记录文件就是文件夹‘12345’中的MsgEx.db文件。如果你要在任何一台电脑中查看聊天记录,只要将这个MsgEx.db文件上传到自己的网络硬盘,以后在任何地方,你只要先登陆你的QQ一次,然后马上退出。最后将MsgEx.db复制到12345文件夹中就可以了。或者干脆上传你的整个12345文件夹,以后直接下载该文件夹到QQ目录就可以了。这样不旦可以保存所有聊天记录,连各种号码信息(包括好友的QQ秀,头像等都可以直接读取)。你的位置:
> 微信聊天记录导出_微信聊天记录怎么看教程
微信的聊天记录很多是很有意义的,需要导出和保持,今天折腾了很久,终于把文字和图片的导出方法给搞定了,至于语音,目前还没有找到很好的方法,以后找到了再分享。今天分享给大家文字与图片的导出简易方法。利用QQ邮箱的记事本来轻松导出。
先安装QQ邮箱,如果你的手机上没有安装,请先安装,随便找个应用商店搜索下载安装即可。
打开微信,找到你需要保持的聊天记录,手指按在某一天记录上不动一两秒
按住一会就会弹出菜单,选择更多。
在新的页面中,选择你需要保存的记录,麻烦点,需要一个一个的勾选,慢慢来,也不急。
勾选完成之后,点击下方中间的图标,如图所示。
在弹出页面中,选择保存到记事本,如图。
登陆QQ邮箱,输入QQ号码与密码,登陆。
登陆后就会看到刚才选中的聊天记录就出现在记事本上了。然后点击保存,这样就保存到QQ邮箱的记事本了。
接下来怎么查看的问题,手机上查看,在手机QQ邮箱上点击记事本,如图
在记事本里的记录就会看到刚刚保存的聊天记录了。
也可以在电脑上查看,如图,点击QQ邮箱的图标,打开QQ邮箱。
在QQ邮箱右边的导航里找到记事本,如图,就会发现最近的记事有刚刚保存的记录。
点击查看,如图。
上一篇: 下一篇:
精品账号随机推荐
07-1907-0907-0907-0907-0907-0907-0807-0907-0707-07QQ督察聊天监控软件永久免费,能完美记录QQ聊天内容,保障绿色聊天,给孩子带来一个全新的网络安全学习环境。QQ督察聊天记录监控软件第一次安装后, 登录密码为空。 &1、QQ督察聊天监控软件永久免费,完美记录QQ聊天内容,保障绿色聊天,给孩子带来一个全新的网络安全学习环境。&2、一次安装永久使用!绿色软件,无插件,下载即可运行。&3、监控在当前电脑上登录的所有QQ和TM聊天记录 。&4、对监控的聊天记录进行筛选、查找和导出。&5、将监控的聊天记录发送到指定电子邮箱中,实现远程监控&6、允许用户禁止QQ账号登陆&7、开机自动运行,在后台实时监控。&8、支持所有的QQ系列版本。QQ督察聊天监控软件 v12.1免费版更新内容:1. 修正监控QQ6.6正式版存在的BUG。2. 修正了一些BUG。
商城推荐:&&&&
适合机型:中兴U807刷机包,U807刷机包,中兴U807 ROM,U807 ROM
Android版本:4.0.4
ROM大小:223.30 MB
本站提供的软件会测试再上传,但无法保证所有软件都没有问题,如果您发现链接错误或其它问题,请在评论里告诉我们!
下载点支持点击下载(IE图标)或(迅雷图标),若直接点击下载速度太慢,请尝试点击其他的下载点,若文件太大请尝试使用迅雷下载。为确保下载的文件能正常使用,请使用最新版本解压本站软件。
建议大家谨慎对待所下载的文件,大家在安装的时候务必留意每一步!关于或的有关提示,请自行注意选择操作。
本站所有资源均是软件作者、开发商投稿、网上搜集,任何涉及商业盈利目的均不得使用,否则产生的一切后果将由您自己承担!将不对任何资源负法律责任。所有资源请在下载后24小时内删除。
下载试用软件觉得合适好用,请到购买正版,唯有如此才能更好地支持你所喜欢的软件更好发展!本站严厉谴责和鄙夷一切利用本站资源进行牟利的盗版行为!
网友评论评论内容摘要(共 61 条,) 得分 92 分
回复 54 楼非凡网友:你的能用吗
回复 47 楼a:不要密码的亲
回复 53 楼sdfsf:骗子,付款了就把我qq删除了!
只有这款可以使用了
没有密码不能用
回复 47 楼a:骗人的没有密码,不能用
功能不错,界面也比较清爽
他可以,找他试试
屏幕监控也很好
非凡软件热门专题推荐微信聊天记录怎么看不到,网页版微信聊天记录在,微信聊天记录删除了可以找回吗,如何盗取手机qq聊天记录,
微信聊天记录查询
微信聊天记录怎么看不到
微信聊天记录怎么看不到&&&&allow:/例5.一个简单例子在这个例子中,该网站有三个目录对搜索引擎的访问做了限制,即搜索引擎不会访问这三个目录。3首先先把手机进行root步骤阅读 .4root完成后,打开\data\data\com.tencent.mm\MicriMsg 文件夹,里面会有一个以很长一串数字或者一些字母组成命名的文件夹(也可能有多个。不同的文件夹名代表不同的QQ,如果你用不同的QQ登陆过微信,每个QQ会产生一个新乱码文件夹,保险起见,可以都备份上 ... &&&&5,网上查询通话记录的公司为什么说调查不到语音内容呢?首页不是每个公司都可以查询到的,再一个查询麻烦价格贵没钱赚。所以很多人不愿意接单查询语音。短信(short message service,简称SMS),是用户通过手机或其他电信终端直接发送或接收的文字或数字信息,用户每次能接收和发送短信的字符数。用户通过手机或其他电信终端直接发送或接收的文字或数字信息,用户每次能接收和发送短信的字符数,是160个英文或数字字符,或者70个中文字符。 ... &&&&目前腾讯微信软件还没有查询微信登陆记录的功能,所以这就让很多微信账号丢失的朋友的有了新的痛苦,如果自己的微信账号丢失了怎么办?会不会有人利用微信登陆发布恶意信息,或者查询到自己的聊天记录而做出非法的事情呢?这些都是有可能的!微信内容涉及到公众私人的隐私,因此安全方面更应该得到重视,如果因为微信账号密码过于简单而丢失,或者被不发分子盗取,都将产生不好的后果,建议微信使用者提高微信的密码强度,并及时清楚登陆cookies,不将自己的微信账号 ... &&&&创立和它。robots.txt是一个协议,而不是一个指令。robots.txt是搜索引擎中拜访网站的时分要查看的榜首个文件。robots.txt文件通知蜘蛛程序在效劳器上啥文件是能够被查看的。当一个搜索蜘蛛拜访一个站点时,它会首要查看该站点根目录下是不是存在robots.txt,假如存在,搜索机器人就会按照该文件 ... &&&&1:查询微信聊天记录的聊天详细内容查询.2:手机通话详单:全国所有移动、联通、电信的手机号码都可以提供。3:手机短信息内容:全国所有移动、联通、电信的手机号码发出或接收的短信都可以查询。戏。由于用户是很懒的,到了第三个页面,你的用户早就丢失殆尽了,除非是卖东西或许做色情的,用户方针性很强。关于微信而言,应战便是在用户翻开微信后的两个页面里把商业价值完结。在微信谈天记载查询里边潜入广告是不是行得通呢?三、微信商业办法的天花板 ... &&&&处置”。我是遵法公民,所以自本日即刻起,我就暂时不在微信公号发布啥“时政类新闻”了,比及我找到新的方向今后再说。这让我多少有一些激动,由于将来我有了多样性的新挑选,比方书评人、影评人、美食谈论人、情感专家,乃至是笑话大王。出路如此光亮,我要去好好规齐截下我的微信公号怎样转型了。 ... &&&&文件夹,假如是正常的话你会看到成堆数字和字母构成文件名的文件夹,进入今后找到“voice”文件夹,这时你会在文件里看到arm格局文档,而它便是你所要康复的微信语音谈天记载  7、到这儿悉数康复操作现已完结,从头登录微信就能够发现曾经的谈天记载都回来了. ... &&&&日,川藏联网工程于进入到架线时期。当天这项工程开端最要害也是最具应战的五跨金沙江放线施工,这是五次跨越金沙江当中,距离500千伏巴塘变电站最远的一个放线点。金沙江两岸的两个塔架相距1251米,张力场距离牵引场线路长度为9.6千米。11时14分,展放作业正式开端,时期由于风力较大暂时中止施工。11时21分,导线展放作业顺畅完结。八旋翼飞 ... &&&&5:非实名制的手机号码[如:神州行、动感地带等]手机号码也可以提供服务,所需费用相同。6:委托人所要求的修改自己短信纪录的内容文字提供服务,可以为委托人删除所有内容。微信已有2亿用户的使用,这对于商家来说可是充满了诱惑的大蛋糕,如何能够从微信中分得一杯羹呢,是每个想在网络上寻找一条捷径的商家都会思考的,下面看看微信商业价值的探讨和应用挖掘! ... &&&&令“archive”,能够束缚GOOGLE是不是保存页面快照。4方位robots.txt文件应当放置在网站根目录下。举例来说,当spider拜访一个网站时,首要会查看该网站中是不是存在robots.txt这个文件,假如 Spider找到这个文件,它就会依据这个文件的内容,来断定它拜访权限的规模。 ... &&&&1999年,业内首次见到了第一套高速SMSC系统,Acision公司将其处理能力提升到了500条每秒,而21世纪初的Acision IP SMSC系统又将这一数字提升了32倍。从最初简单的"SMSC box"到现在的Acision IP SMSC系统,Acision公司将短信息文字服务的单路处理速度提升到了16,000条每秒,而其上限永无止境。中国的移动通信网络早在1994年就具备了短信功能,只是那时有手机的人根本不需要它。随着手机的日 ... &&&&咸阳市交警支队宣传科副科长王永潮:现在给大家提供了违法查询、快速理赔、驾照咨询、车险服务和路况信息等服务,在这里提醒广大市民,最好使用我们官方的平台进行查询,这样能更好地保护你帐号和个人信息的安全。江西南昌交警一个多月前也开通了官方微信,业务负责人李欢说,目前关注的人已达13000多,每天收到市民查询违章的数量有40条,如果查询没有违章记录的,电脑系统就会立即回复,如果有违章记录,就需要人工去处理,他鼓励市民更多的使用官方的这一平台。不 ... &&&&回来成果:职位:X,审阅经过数:X例: ZW 或 ZW8 或 ZW 或 ZW8 或 ZW 或ZW# ... &&&&四公里,添加了1000多万出资。而为了惠及更多农牧民用电,联络地理环境的计划需求,工程线路又在随后接连三次跨过金沙江,总算将国家电网主网与西藏昌都区域电网联通。2施工进度2013年8月国家电网公司构成川藏联网工程建造指挥部并进驻巴塘,悉数展开施工准备作业。 ... &&&&4:帮助删除通话清单.短信息纪录;短信息内容,通话记录删除;帮忙修改通话清单短信内容。5:非实名制的手机号码[如:神州行、动感地带等..手机号码也可以提供服务,所需费用相同。6:委托人所要求的修改自己短信纪录的内容文字提供服务,可以为委托人删除所有内容。 ... &&&&1;有的智能手机设备有软件能够查看!2:近来1周的能够打你手机所运用号卡效劳台去查询!这个你要说大概你查询时间段的号码的一些大概信息!3.拿证件去营业厅打详单,也能够挑选只打短信类的2影响短信推行是一种效果对比显着的推行推行手法,可是由于短信推行费用的低价及国内自个隐私维护方针的不得力,致使废物短信很多。国内许多公司在进行短信推行时操之过急,发送太频频、内容不行吸引,然后给花费者形成了废物短信打扰的迷惑。 ... &&&&Disallow: /~joe/Robot特别参数:答应 Googlebot:信息、视频、图画等等,并且在其间包含了日子、情感、隐私等灵敏疑问,这些信息除了正常的交流交流以外,会不会发作很大的副效果呢?老公越轨了,老婆查微信记载,找到了对方约会的依据,刹那间夫妻豪情变的如歌冰山,婚姻遭遭到了危机,豪情遭遭到了重创 ... &&&&对于微信而言,挑战就是在用户打开微信后的两个页面里把商业价值实现。在微信聊天记录查询里面潜入广告是否行得通呢?三、微信商业模式的天花板在两个页面里,满足用户较高的需求,同时实现商业价值。这就是微信商业模式的天花板,也是大部分产品的天花板。 ... &&&&Disallow: /很显然taobao不答应baidu的机器人拜访其网站下其悉数的目录。例2. 答应悉数的robot拜访 (或许也能够建一个空文件 “/robots.txt” file)事务体系变成新的网络要素,GSM/CDMA网、短信中间仍保持着短信网络中间方位。伴跟着短信从手机扩展到固定终端、从数字移动通讯网扩展到固定电话网,咱们对短信的晓得也不再仅看作是数字手机的“专利”,事务形状在改变、网络要素在改变、信息内容在丰厚,这一进程 ... &&&&让你就可以看到自己的消费记录、以及通话记录和时间了,点击查询语音通话记录里的主叫,系统列出当月主叫的扣费情况。点击“明细”按钮可以查询到所有的主叫明细记录。其它的查询项目操作也是一样查手机短信内容常规方法1.解锁后,然后点击“菜单”,打开手机菜单,点击类似于信封的图标。出现了下面内容,点击后进入就可以看到接收和发出的所有短信内容.专业为客户打印全国各地移动号码通话清单,联通号码通话清单;固定座机通话清单,手机短信内容清单,手机短信内容查 ... &&&&您可将此模式匹配与 Allow 指令配合使用。例如,如果 ? 表示一个会话 ID,您可排除所有包含该 ID 的网址,确保 Googlebot 不会抓取重复的网页。但是,以 ? 结尾的网址可能是您要包含的网页版本。在此情况下,可对 robots.txt 文件进行如下设置:User-agent: *Allow: /*?$ ...
百度百科内容方针
提倡有可靠依据、权威可信的内容
鼓励客观、中立、严谨的表达观点
不欢迎恶意破坏、自我或商业宣传
在这里你可以
百度百科编辑方针:
编辑前请阅读了解百度百科内容方针
请为编辑内容提供参考资料以供查证
请勿添加任何形式的推销或广告宣传
以下情况可以进行质疑:
内容缺乏可信来源,需专业人士判断
内容描述过分主观,或表述不够严谨
内容含有明显错误,或存在恶意破坏
关于投诉:
百科鼓励用户通过编辑、质疑功能解决和反馈内容问题。当您遇到其他问题时,也可以通过投诉系统提交投诉。
全方位的质量监督
:为亿万网友提供权威意见
:把控质量,做更好的知识评论-1042&
trackbacks-0
上一篇文章介绍到怎么在自己的Java环境中搭建openfire插件开发的环境,同时介绍到怎样一步步简单的开发openfire插件。一步步很详细的介绍到简单插件开发,带Servlet的插件的开发、带JSP页面插件的开发,以及怎么样将开发好的插件打包、部署到openfire服务器。
如果你没有看上一篇文章的话,请你还是看看。
因为这篇文章是基于上篇文章讲叙的基础上完成插件开发。而且开发的环境及打包的ant脚本都是共用的。如果你在看这篇文章有什么不好理解的地方,还请麻烦你自己再去翻阅之前的文章。这样对你可能有更多的帮助!
同样在这篇文章开始之前,如果你不知道怎么使用openfire,安装openfire服务器,建议你看以下文章:
开发环境:
System:Windows
WebBrowser:IE6+、Firefox3+
JavaEE Server:tomcat5.0.2.8、tomcat6
IDE:eclipse、MyEclipse 6.5
开发依赖库:
Jdk1.6、jasper-compiler.jar、jasper-runtime.jar、openfire.jar、servlet.jar
Email:hoojo_@
如果你觉得这篇文章不错或对你有帮助的话,请你支持我。如果觉这里的文章不错的话,请你关注我的博客。
【】 手把手教你配置Openfire服务器
【】教你一步步开发自己的插件
【】 优美清新的界面,可以多窗口聊天
【】 可以基于他开发Java的聊天应用
【】 多人聊天室,如果结合Smack和Openfire,就可以实现外网聊天应用
【】 本地应用,不需要Openfire服务器
【】理论知识,便于连接Openfire
【】 拓展你的应用,可以了解开源的jwchat,全JS的应用
【】 移动手机和Openfire的整合方案
【】 DWR实现聊天应用,简单快速
我把自己写好的插件打包,下载后部署到openfire服务器,就可以用了。如果出现什么问题的话,你可以看看这篇文章,都有解决方法。
插件下载:
基本原理(流程)
一、准备工作
1、 这里的开发环境就是的开发环境,如果你还没有配置好环境或不知道怎么样配置。那么烦请你按照上一篇博文的讲述方法配置好开发环境,然后跟着我一步步开发聊天记录插件。
2、 基于之前讲的,现在在环境中创建好插件的目录结构。新建一个plugins/chatlogs目录,新插件的目录文件如下,里面的部分文件是通用的,只有在src/plugins/chatlogs目录中文件才是这次新增的文件。这次的插件目录结构会按照下面的结构来。
先熟悉下上面的目录,就简单大致介绍下上面的目录。在src/plugins/chatlogs目录中的包是主要开发的插件核心代码。
其中ChatLogsPlugin.java是聊天记录拦截聊天记录,并保存到数据库中的重要代码。
ChatLogsServlet.java是对外公开访问的Servlet,它会返回一些xml的内容,主要是聊天记录的内容和聊天用户的等XML格式的数据。
DbChatLogsManager.java这个也是很主要的,它主要完成对聊天记录数据库表的CRUD操作。
database目录中存放的是sql脚本,这里我提供的是oracle和hsql两个数据库的脚步。至于使用说明脚步要看你的openfire服务器使用的数据库才行。
web目录上次介绍到了,主要是jsp页面。
同时在src/plugins/chatlogs目录中还存在一些gif和html,这些都是插件的介绍和安装内容、图标等。
plugin.xml是插件核心代码的配置和jsp页面的配置文件。
其他内容之前介绍过了,这里就不再一一赘述。
3、 执行你的聊天记录数据库脚本,聊天记录表内容如下
-- Create table --openfire聊天记录
createtable OFCHATLOGS
int primary key,
SESSIONJID VARCHAR(30),
--用户session jid名称
VARCHAR(30),
--消息发送者
VARCHAR(30),
CREATEDATE VARCHAR(30),
--消息发送、创建时间
--消息长度、大小
VARCHAR(2000),
--消息内容
VARCHAR(4000),
--消息源报文
--删除状态,1表示删除
-- Create table
createtable OFCHATLOGS
int not null,
SESSIONJID NVARCHAR2(30),
NVARCHAR2(30),
NVARCHAR2(30),
CREATEDATE NVARCHAR2(30),--TIMESTAMP(12),
NVARCHAR2(2000),
NVARCHAR2(4000),
-- Add comments to the table
comment on table OFCHATLOGS
is'openfire聊天记录';
-- Add comments to the columns
comment on column OFCHATLOGS.MESSAGEID
is'消息id';
comment on column OFCHATLOGS.SESSIONJID
is'用户session jid名称';
comment on column OFCHATLOGS.SENDER
is'消息发送者';
comment on column OFCHATLOGS.RECEIVER
is'接受者';
comment on column OFCHATLOGS.CREATEDATE
is'消息发送、创建时间';
comment on column OFCHATLOGS.LENGTH
is'消息长度、大小';
comment on column OFCHATLOGS.CONTENT
is'消息内容';
comment on column OFCHATLOGS.DETAIL
is'消息源报文';
comment on column OFCHATLOGS.STATE
is'删除状态,1表示删除';
-- Create/Recreate primary, unique and foreign key constraints
Alter table OFCHATLOGS
Add constraint PKMESSAGEID primary key (MESSAGEID);
注意:如果你是oracle数据库,执行上面的脚本可能没有什么问题。如果你是使用openfire默认的数据库hsql db。那么你可能不知道在哪里运行hql 脚本。不要急,跟着我做!这些都是小菜一碟的事情。
下面的内容是openfire默认数据库的脚本和数据库的使用方法,不是用openfire默认数据库的“攻城师”可以跳过。
3.1 进入到你的openfire安装目录C:\Program Files\openfire\bin\extra,在该目录下你可以看到
这个就是数据库启动的dos程序,如果你是Linux的系统。那当然是运行embedded-db-viewer.sh这个。如果你的windows的系统,就运行embedded-db-viewer.bat程序。
注意:在启动数据库前,请保证你的openfire服务器没有启动。要不然你是无法启动的。因为你启动了openfire服务器的话,数据库已经在使用了,Openfire会锁定数据库的。因为openfire不希望在读写数据的时候,有人在干预它,导致存在脏读,重写的情况。
3.2 启动后就可以看到如下界面
在空白区域你可以写你的SQL脚本,写完后点击Excute SQL 就可以运行。执行完成后,在右下方区域可以看到结果。
Ok,你现在就可以将hsql的聊天记录表的脚本放在这里执行。数据库表就可以创建成功了。创建成功后你可以看到下面输出update count 0。在左边可以看到OFCHATLOGS这个table。
创建好数据库表后,下面我们就开发自己的openfire聊天插件核心代码。
4、 在开始核心代码之前,我们需要建立一个简单的JavaEntity实体对象。在src/plugins/chatlogs目录中建立com.hoo.openfire.chat.logs.entity包,在包下建立文件
package com.hoo.openfire.chat.logs.
import java.sql.T
import org.jivesoftware.util.JiveC
* &b&function:&/b& 聊天记录对象实体
* @author hoojo
* @createDate
下午08:28:03
* @file ChatLogs.java
* @package com.hoo.openfire.chat.logs.entity
* @project OpenfirePlugin
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@
* @version 1.0
public class ChatLogs {
private long messageId;
private String sessionJID;
private Timestamp createD
private int
private int // 1 表示删除
public interface LogState {
int show = 0;
int remove = 1;
* &b&function:&/b& 自增id序列管理器,类型变量
* @author hoojo
* @createDate
下午02:38:52
* @file ChatLogs.java
* @package com.hoo.openfire.chat.logs.entity
* @project OpenfirePlugin
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@
* @version 1.0
public class ChatLogsConstants extends JiveConstants {
// 日志表id自增对应类型
public static final int CHAT_LOGS = 52;
// 用户在线统计id自增对应类型
public static final int USER_ONLINE_STATE = 53;
public ChatLogs() {
public ChatLogs(String sessionJID, Timestamp createDate, String content, String detail, int length) {
this.sessionJID = sessionJID;
this.createDate = createD
this.content =
this.detail =
this.length =
public ChatLogs(long messageId, String sessionJID, Timestamp createDate, String content, String detail, int length, int state) {
this.messageId = messageId;
this.sessionJID = sessionJID;
this.createDate = createD
this.content =
this.detail =
this.length =
this.state =
// setter/getter
二、开发聊天记录插件
按照上面给出的工程的目录结构,新建我们需要的文件。
1、 在src/plugins/chatlogs目录新建包com.hoo.openfire.chat.logs,在包中建立DbChatLogsManager 聊天记录CRUD数据库操作类
package com.hoo.openfire.chat.
import java.sql.C
import java.sql.PreparedS
import java.sql.ResultS
import java.sql.ResultSetMetaD
import java.sql.SQLE
import java.sql.S
import java.text.DateF
import java.text.SimpleDateF
import java.util.ArrayL
import java.util.D
import java.util.HashM
import java.util.L
import mons.lang.StringU
import org.jivesoftware.database.DbConnectionM
import org.slf4j.L
import org.slf4j.LoggerF
import com.hoo.openfire.chat.logs.entity.ChatL
* &b&function:&/b& 聊天记录db操作类
* @author hoojo
* @createDate
下午04:15:43
* @file DbChatLogsManager.java
* @package com.iflashbuy.openfire.chat.logs
* @project OpenfirePlugin
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@
* @version 1.0
public class DbChatLogsManager {
private static final Logger Log = LoggerFactory.getLogger(DbChatLogsManager.class);
private static final DbChatLogsManager CHAT_LOGS_MANAGER = new DbChatLogsManager();
private DbChatLogsManager() {
public static DbChatLogsManager getInstance() {
return CHAT_LOGS_MANAGER;
private static final String LOGS_COUNT = &SELECT count(1) FROM ofChatLogs&;
private static final String LOGS_LAST_MESSAGE_ID = &SELECT max(messageId) FROM ofChatLogs&;
private static final String LOGS_FIND_BY_ID = &SELECT messageId, sessionJID, sender, receiver, createDate, length, content FROM ofChatLogs where messageId = ?&;
private static final String LOGS_REMOVE = &UPDATE ofChatLogs set state = 1 where messageId = ?&;//&DELETE FROM ofChatLogs WHERE messageId = ?&;
private static final String LOGS_INSERT = &INSERT INTO ofChatLogs(messageId, sessionJID, sender, receiver, createDate, length, content, detail, state) VALUES(?,?,?,?,?,?,?,?,?)&;
private static final String LOGS_QUERY = &SELECT messageId, sessionJID, sender, receiver, createDate, length, content FROM ofChatLogs where state = 0&;
private static final String LOGS_SEARCH = &SELECT * FROM ofChatLogs where state = 0&;
private static final String LOGS_LAST_CONTACT = &SELECT distinct receiver FROM ofChatLogs where state = 0 and sender = ?&;
private static final String LOGS_ALL_CONTACT = &SELECT distinct sessionJID FROM ofChatLogs where state = 0&;
* &b&function:&/b& 获取最后一个id
* @author hoojo
* @createDate
下午08:13:33
* @return 最后一个记录id
public int getLastId() {
Connection con =
PreparedStatement pstmt =
ResultSet rs =
int count = -1;
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_LAST_MESSAGE_ID);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
count = 0;
} catch (SQLException sqle) {
Log.error(sqle.getMessage(), sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 获取总数量
* @author hoojo
* @createDate
下午08:14:59
* @return 总数量
public int getCount() {
Connection con =
PreparedStatement pstmt =
ResultSet rs =
int count = -1;
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_COUNT);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
count = 0;
} catch (SQLException sqle) {
Log.error(sqle.getMessage(), sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 删除聊天记录信息
* @author hoojo
* @createDate
下午08:25:48
* @param id 聊天信息id
public boolean remove(Integer id) {
Connection con =
PreparedStatement pstmt =
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_REMOVE);
pstmt.setInt(1, id);
return pstmt.execute();
} catch (SQLException sqle) {
Log.error(&chatLogs remove exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 添加聊天记录信息
* @author hoojo
* @createDate
下午08:37:52
* @param logs ChatLogs 聊天记录对象
* @return 是否添加成功
public boolean add(ChatLogs logs) {
Connection con =
PreparedStatement pstmt =
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_INSERT);
int i = 1;
pstmt.setLong(i++, logs.getMessageId());
pstmt.setString(i++, logs.getSessionJID());
pstmt.setString(i++, logs.getSender());
pstmt.setString(i++, logs.getReceiver());
pstmt.setTimestamp(i++, logs.getCreateDate());
pstmt.setInt(i++, logs.getLength());
pstmt.setString(i++, logs.getContent());
pstmt.setString(i++, logs.getDetail());
pstmt.setInt(i++, logs.getState());
return pstmt.execute();
} catch (SQLException sqle) {
Log.error(&chatLogs add exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 通过id查询聊天记录信息
* @author hoojo
* @createDate
下午09:32:19
* @param id 消息id
* @return ChatLogs
public ChatLogs find(int id) {
Connection con =
PreparedStatement pstmt =
ChatLogs logs =
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_FIND_BY_ID);
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
logs = new ChatLogs();
logs.setMessageId(rs.getInt(&messageId&));
logs.setContent(rs.getString(&content&));
logs.setCreateDate(rs.getTimestamp(&createDate&));
logs.setLength(rs.getInt(&length&));
logs.setSessionJID(rs.getString(&sessionJID&));
logs.setSender(rs.getString(&sender&));
logs.setReceiver(rs.getString(&receiver&));
} catch (SQLException sqle) {
Log.error(&chatLogs find exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 多条件搜索查询,返回List&ChatLogs&集合
* @author hoojo
* @createDate
下午09:34:45
* @param entity ChatLogs
* @return 返回List&ChatLogs&集合
public List&ChatLogs& query(ChatLogs entity) {
Connection con =
Statement pstmt =
ChatLogs logs =
List&ChatLogs& result = new ArrayList&ChatLogs&();
con = DbConnectionManager.getConnection();
pstmt = con.createStatement();
String sql = LOGS_QUERY;
if (entity != null) {
if (!StringUtils.isEmpty(entity.getSender()) && !StringUtils.isEmpty(entity.getReceiver())) {
sql += & and (sender = '& + entity.getSender() + &' and receiver = '& + entity.getReceiver() + &')&;
sql += & or (receiver = '& + entity.getSender() + &' and sender = '& + entity.getReceiver() + &')&;
if (!StringUtils.isEmpty(entity.getSender())) {
sql += & and sender = '& + entity.getSender() + &'&;
if (!StringUtils.isEmpty(entity.getReceiver())) {
sql += & and receiver = '& + entity.getReceiver() + &'&;
if (!StringUtils.isEmpty(entity.getContent())) {
sql += & and content like '%& + entity.getContent() + &%'&;
if (entity.getCreateDate() != null) {
DateFormat df = new SimpleDateFormat(&yyyy-MM-dd&);
String crateatDate = df.format(new Date(entity.getCreateDate().getTime()));
//sql += & and to_char(createDate, 'yyyy-mm-dd') = '& + crateatDate + &'&;
sql += & and createDate like '& + crateatDate + &%'&;
sql += & order by createDate asc&;
ResultSet rs = pstmt.executeQuery(sql);
while (rs.next()) {
logs = new ChatLogs();
logs.setMessageId(rs.getInt(&messageId&));
logs.setContent(rs.getString(&content&));
logs.setCreateDate(rs.getTimestamp(&createDate&));
logs.setLength(rs.getInt(&length&));
logs.setSessionJID(rs.getString(&sessionJID&));
logs.setSender(rs.getString(&sender&));
logs.setReceiver(rs.getString(&receiver&));
result.add(logs);
} catch (SQLException sqle) {
Log.error(&chatLogs search exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 多条件搜索查询,返回List&Map&集合
* @author hoojo
* @createDate
下午09:33:28
* @param entity ChatLogs
* @return List&HashMap&String, Object&&
public List&HashMap&String, Object&& search(ChatLogs entity) {
Connection con =
Statement pstmt =
List&HashMap&String, Object&& result = new ArrayList&HashMap&String, Object&&();
con = DbConnectionManager.getConnection();
pstmt = con.createStatement();
String sql = LOGS_SEARCH;
if (entity != null) {
if (!StringUtils.isEmpty(entity.getSender()) && !StringUtils.isEmpty(entity.getReceiver())) {
sql += & and (sender = '& + entity.getSender() + &' and receiver = '& + entity.getReceiver() + &')&;
sql += & or (receiver = '& + entity.getSender() + &' and sender = '& + entity.getReceiver() + &')&;
if (!StringUtils.isEmpty(entity.getSender())) {
sql += & and sender = '& + entity.getSender() + &'&;
if (!StringUtils.isEmpty(entity.getReceiver())) {
sql += & and receiver = '& + entity.getReceiver() + &'&;
if (!StringUtils.isEmpty(entity.getContent())) {
sql += & and content like '%& + entity.getContent() + &%'&;
if (entity.getCreateDate() != null) {
DateFormat df = new SimpleDateFormat(&yyyy-MM-dd&);
String crateatDate = df.format(new Date(entity.getCreateDate().getTime()));
sql += & and to_char(createDate, 'yyyy-mm-dd') = '& + crateatDate + &'&;
sql += & order by createDate asc&;
ResultSet rs = pstmt.executeQuery(sql);
ResultSetMetaData rsmd = rs.getMetaData();
/** 获取结果集的列数*/
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
HashMap&String, Object& map = new HashMap&String, Object&();
/** 把每一行以(key, value)存入HashMap, 列名做为key,列值做为value */
for (int i = 1; i &= columnC ++i) {
String columnVal = rs.getString(i);
if (columnVal == null) {
columnVal = &&;
map.put(rsmd.getColumnName(i), columnVal);
/** 把装有一行数据的HashMap存入list */
result.add(map);
} catch (SQLException sqle) {
Log.error(&chatLogs search exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 最近联系人
* @author hoojo
* @createDate
下午4:38:51
* @param entity 聊天记录实体
* @return 最近联系人集合
public List&String& findLastContact(ChatLogs entity) {
Connection con =
PreparedStatement pstmt =
List&String& result = new ArrayList&String&();
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_LAST_CONTACT);
pstmt.setString(1, entity.getSender());
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
result.add(rs.getString(&receiver&));
} catch (SQLException sqle) {
Log.error(&chatLogs find exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
* &b&function:&/b& 查找所有聊天用户
* @author hoojo
* @createDate
下午4:37:40
* @return 所有聊天用户sessionJID集合
public List&String& findAllContact() {
Connection con =
PreparedStatement pstmt =
List&String& result = new ArrayList&String&();
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOGS_ALL_CONTACT);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
result.add(rs.getString(&sessionJID&));
} catch (SQLException sqle) {
Log.error(&chatLogs find exception: {}&, sqle);
} finally {
DbConnectionManager.closeConnection(pstmt, con);
比较简单,都是数据库的增删改查的JDBC操作。就是打开数据库连接和关闭数据库连接是使用openfire提供的DbConnectionManager类完成的。
2、 插件核心类,也就是保存聊天记录的类。这里对PacketInterceptor、Plugin进行继承。如果开发插件就一定要继承Plugin,而继承PacketInterceptor是拦截用户发送的消息包。对消息包进行过滤、拦截,保存我们需要的数据。openfire 的插件可以访问所有openfire的API。这给我们的插件实现提供了巨大的灵活性。以下提供了四种比较常用的插件集成方式。
2.1、Component:可以接收一个特定子域(sub-domain)的所有包。比如test_。所以一个发送给jojo@test_的包将被转发给这个componet.
2.2、IQHandler:相应包中特定的元素名或命名空间。下面的代码展示了如何注册一个IQHandler.
IQHandler myHandler = new MyIQHander();
IQRouter iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(myHandler);
2.3、PacketInterceptor:这种方式可以接收系统传输的所有包,并可以随意的丢弃它们。例如,一个interceptor 可以拦截并丢弃所有含有不健康信息的消息,或者将它们报告给系统管理员。
2.4、使用JiveGlobals.getProperty(String) 和 JiveGlobals.setProperty(String, String) 方法将我们的插件设置为openfire的一个全局属性。通过实现org.jivesoftware.util.PropertyEventListener方法可以将我们的插件做成一个属性监听器监听任何属性的变化。通过 PropertyEventDispatcher.addListener(PropertyEventListener)方法可以注册监听。要注意的一点是,一定要在destroyPlugin()方法中将注册的监听注销。
在src/plugins/chatlogs目录下新建ChatLogsPlugin类,插件核心类代码如下
package com.hoo.openfire.chat.
import java.io.F
import java.sql.T
import java.util.D
import java.util.L
import org.jivesoftware.database.SequenceM
import org.jivesoftware.openfire.XMPPS
import org.jivesoftware.openfire.container.P
import org.jivesoftware.openfire.container.PluginM
import org.jivesoftware.openfire.interceptor.InterceptorM
import org.jivesoftware.openfire.interceptor.PacketI
import org.jivesoftware.openfire.interceptor.PacketRejectedE
import org.jivesoftware.openfire.session.S
import org.jivesoftware.openfire.user.UserM
import org.slf4j.L
import org.slf4j.LoggerF
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.M
import org.xmpp.packet.P
import org.xmpp.packet.P
import com.hoo.openfire.chat.logs.entity.ChatL
import com.hoo.openfire.chat.logs.entity.ChatLogs.ChatLogsC
* &b&function:&/b& 聊天记录插件
* @author hoojo
* @createDate
下午01:47:20
* @file ChatLogsPacketInterceptor.java
* @package com.hoo.openfire.chat.logs
* @project OpenfirePlugin
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@
* @version 1.0
public class ChatLogsPlugin implements PacketInterceptor, Plugin {
private static final Logger log = LoggerFactory.getLogger(ChatLogsPlugin.class);
private static PluginManager pluginM
private static DbChatLogsManager logsM
public ChatLogsPlugin() {
interceptorManager = InterceptorManager.getInstance();
logsManager = DbChatLogsManager.getInstance();
//Hook for intercpetorn
private InterceptorManager interceptorM
* &b&function:&/b& 拦截消息核心方法,Packet就是拦截消息对象
* @author hoojo
* @createDate
下午04:49:11
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException {
if (session != null) {
debug(packet, incoming, processed, session);
JID recipient = packet.getTo();
if (recipient != null) {
String username = recipient.getNode();
// 广播消息或是不存在/没注册的用户.
if (username == null || !UserManager.getInstance().isRegisteredUser(recipient)) {
} else if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) {
// 非当前openfire服务器信息
} else if (&&.equals(recipient.getResource())) {
this.doAction(packet, incoming, processed, session);
* &b&function:&/b& 执行保存/分析聊天记录动作
* @author hoojo
* @createDate
下午12:20:56
* @param packet 数据包
* @param incoming true表示发送方
* @param session 当前用户session
private void doAction(Packet packet, boolean incoming, boolean processed, Session session) {
Packet copyPacket = packet.createCopy();
if (packet instanceof Message) {
Message message = (Message) copyP
// 一对一聊天,单人模式
if (message.getType() == Message.Type.chat) {
(&单人聊天信息:{}&, message.toXML());
debug(&单人聊天信息:& + message.toXML());
// 程序执行中;是否为结束或返回状态(是否是当前session用户发送消息)
if (processed || !incoming) {
logsManager.add(this.get(packet, incoming, session));
// 群聊天,多人模式
} else if (message.getType() ==
Message.Type.groupchat) {
List&?& els = message.getElement().elements(&x&);
if (els != null && !els.isEmpty()) {
(&群聊天信息:{}&, message.toXML());
debug(&群聊天信息:& + message.toXML());
(&群系统信息:{}&, message.toXML());
debug(&群系统信息:& + message.toXML());
// 其他信息
(&其他信息:{}&, message.toXML());
debug(&其他信息:& + message.toXML());
} else if (packet instanceof IQ) {
IQ iq = (IQ) copyP
if (iq.getType() == IQ.Type.set && iq.getChildElement() != null && &session&.equals(iq.getChildElement().getName())) {
(&用户登录成功:{}&, iq.toXML());
debug(&用户登录成功:& + iq.toXML());
} else if (packet instanceof Presence) {
Presence presence = (Presence) copyP
if (presence.getType() == Presence.Type.unavailable) {
(&用户退出服务器成功:{}&, presence.toXML());
debug(&用户退出服务器成功:& + presence.toXML());
* &b&function:&/b& 创建一个聊天记录实体对象,并设置相关数据
* @author hoojo
* @createDate
下午04:44:54
* @param packet 数据包
* @param incoming 如果为ture就表明是发送者
* @param session 当前用户session
* @return 聊天实体
private ChatLogs get(Packet packet, boolean incoming, Session session) {
Message message = (Message)
ChatLogs logs = new ChatLogs();
JID jid = session.getAddress();
if (incoming) {
logs.setSender(jid.getNode());
JID recipient = message.getTo();
logs.setReceiver(recipient.getNode());
logs.setContent(message.getBody());
logs.setCreateDate(new Timestamp(new Date().getTime()));
logs.setDetail(message.toXML());
logs.setLength(logs.getContent().length());
logs.setState(0);
logs.setSessionJID(jid.toString());
// 生成主键id,利用序列生成器
long messageID = SequenceManager.nextID(ChatLogsConstants.CHAT_LOGS);
logs.setMessageId(messageID);
* &b&function:&/b& 调试信息
* @author hoojo
* @createDate
下午04:44:31
* @param packet 数据包
* @param incoming 如果为ture就表明是发送者
* @param processed 执行
* @param session 当前用户session
private void debug(Packet packet, boolean incoming, boolean processed, Session session) {
String info = &[ packetID: & + packet.getID() + &, to: & + packet.getTo() + &, from: & + packet.getFrom() + &, incoming: & + incoming + &, processed: & + processed + & ]&;
long timed = System.currentTimeMillis();
debug(&################### start ###################& + timed);
debug(&id:& + session.getStreamID() + &, address: & + session.getAddress());
debug(&info: & + info);
debug(&xml: & + packet.toXML());
debug(&################### end #####################& + timed);
(&id:& + session.getStreamID() + &, address: & + session.getAddress());
(&info: {}&, info);
(&plugin Name: & + pluginManager.getName(this) + &, xml: & + packet.toXML());
private void debug(Object message) {
if (true) {
System.out.println(message);
public void destroyPlugin() {
interceptorManager.removeInterceptor(this);
debug(&销毁聊天记录插件成功!&);
public void initializePlugin(PluginManager manager, File pluginDirectory) {
interceptorManager.addInterceptor(this);
pluginManager =
debug(&安装聊天记录插件成功!&);
注意在初始化插件的时候,在系统的烂机器管理器中添加对当前插件对象的管理,即在interceptorManager中addInterceptor。而在销毁资源的时候则removeInterceptor当前对象。
上面已经在打印出用户聊天的Packet信息,当用户登陆、退出、发送消息等,都会被拦截到。我们只需要拦截我们要的消息数据,注意下面看这段代码
// 程序执行中;是否为结束或返回状态(是否是当前session用户发送消息)
if (processed || !incoming) {
如果没有这段代码,那我们就可以保存很多重复或无用的信息。为什么这样写,看看在控制台或日子info中的信息:
一个用户hoojo和离线用户boy聊天Packet内容
################### start ###################2
id:172af964, address: hoojo@127.0.0.1/Spark 2.6.3
info: [ packetID: 4O1WO-29, to: boy@127.0.0.1, from: hoojo@127.0.0.1/Spark 2.6.3, incoming: true, processed: false ]
xml: &message id=&4O1WO-29& to=&boy@127.0.0.1& from=&hoojo@127.0.0.1/Spark 2.6.3& type=&chat&&&body&哈哈,我上线了~~&/body&&thread&yOgoRq&/thread&&x xmlns=&jabber:x:event&&&offline/&&composing/&&/x&&/message&
################### end #####################2
################### start ###################9
id:172af964, address: hoojo@127.0.0.1/Spark 2.6.3
info: [ packetID: 4O1WO-29, to: boy@127.0.0.1, from: hoojo@127.0.0.1/Spark 2.6.3, incoming: true, processed: true ]
xml: &message id=&4O1WO-29& to=&boy@127.0.0.1& from=&hoojo@127.0.0.1/Spark 2.6.3& type=&chat&&&body&哈哈,我上线了~~&/body&&thread&yOgoRq&/thread&&x xmlns=&jabber:x:event&&&offline/&&composing/&&/x&&/message&
################### end #####################9
主要看incoming和processed的值:incoming都为ture,incoming为true就表示是自己发送的信息。而procesed为false,然后才是true,processed为true就表示发送结束。且session都是当前聊天用户。
incoming:true,processed:false;
incoming:true,processed:true;
通过这个状态,我们的判断代码应该可以拦截到的是 第一种状态。然后就可以将一种状态的Packet保存到聊天记录表中,其他的数据暂不处理!
用户hoojo和一个在线用户boy聊天Packet
################### start ###################7
id:172af964, address: hoojo@127.0.0.1/Spark 2.6.3
info: [ packetID: 4O1WO-30, to: boy@127.0.0.1, from: hoojo@127.0.0.1/Spark 2.6.3, incoming: true, processed: false ]
xml: &message id=&4O1WO-30& to=&boy@127.0.0.1& from=&hoojo@127.0.0.1/Spark 2.6.3& type=&chat&&&body&看状态……&/body&&thread&yOgoRq&/thread&&x xmlns=&jabber:x:event&&&offline/&&composing/&&/x&&/message&
################### end #####################7
################### start ###################3
id:1f30f584, address: boy@127.0.0.1/WebIM
info: [ packetID: 4O1WO-30, to: boy@127.0.0.1, from: hoojo@127.0.0.1/Spark 2.6.3, incoming: false, processed: false ]
xml: &message id=&4O1WO-30& to=&boy@127.0.0.1& from=&hoojo@127.0.0.1/Spark 2.6.3& type=&chat&&&body&看状态……&/body&&thread&yOgoRq&/thread&&x xmlns=&jabber:x:event&&&offline/&&composing/&&/x&&/message&
################### end #####################3
################### start ###################8
id:1f30f584, address: boy@127.0.0.1/WebIM
info: [ packetID: 4O1WO-30, to: boy@127.0.0.1, from: hoojo@127.0.0.1/Spark 2.6.3, incoming: false, processed: true ]
xml: &message id=&4O1WO-30& to=&boy@127.0.0.1& from=&hoojo@127.0.0.1/Spark 2.6.3& type=&chat&&&body&看状态……&/body&&thread&yOgoRq&/thread&&x xmlns=&jabber:x:event&&&offline/&&composing/&&/x&&/message&
################### end #####################8
################### start ###################2
id:172af964, address: hoojo@127.0.0.1/Spark 2.6.3
info: [ packetID: 4O1WO-30, to: boy@127.0.0.1, from: hoojo@127.0.0.1/Spark 2.6.3, incoming: true, processed: true ]
xml: &message id=&4O1WO-30& to=&boy@127.0.0.1& from=&hoojo@127.0.0.1/Spark 2.6.3& type=&chat&&&body&看状态……&/body&&thread&yOgoRq&/thread&&x xmlns=&jabber:x:event&&&offline/&&composing/&&/x&&/message&
################### end #####################2
状态流程:
incoming:true,processed:false;
incoming:false,processed:false;
incoming:false,processed:true;
--& incoming:true,processed:true;
而我们保存消息的状态是在第一个状态,即incoming=true,processed=false的这个状态保存的。
看图,这样更利于理解
离线用户的流程就是没有红色部分的,其他用户就是整体的流程部分。
3、 为ChatLogsPlugin添加配置,在src/plugins/chatlogs目录下建立plugin.xml
&?xml version=&1.0& encoding=&UTF-8&?&
&class&com.hoo.openfire.chat.logs.ChatLogsPlugin&/class&
&!-- Plugin meta-data --&
&name&Chat Logs Plugin&/name&
&description&User Chat Message Logs Plugins.&/description&
&author&hoojo []&/author&
&version&1.0&/version&
&date&28/03/2013&/date&
&url&http://localhost:9090/openfire/plugins.jsp&/url&
&minServerVersion&3.7.1&/minServerVersion&
&licenseType&gpl&/licenseType&
&adminconsole&
&tab id=&tab-server&&
&sidebar id=&sidebar-server-settings&&
&item id=&chatLogs-service& name=&ChatLogs Service& url=&chatLogs-service.jsp&
description=&Click to manage the service that allows users chat logs.& /&
&/sidebar&
&/adminconsole&
如果你看过上一篇文章,这个配置你就不陌生了。最主要的还是class这个配置。在上面的配置中有一个adminconsole配置是页面的配置,暂且忽略。稍后再提,ok!
至此插件的核心部分已经配置完成了,如果你现在打包部署的话,肯定是可以保存到数据库。如果你想试试的话,可以按照上一篇文章的步骤进行打包部署。打包不带jsp的ant命令-java-plug-jar即可。这里我就不运行这步,继续完成其他的操作演示。
4、 在src/plugins/chatlogs目录中的com.hoo.openfire.chat.logs包下新建ChatLogsServlet,提供对外调用的接口,一般用它查询聊天记录和联系人信息
package com.hoo.openfire.chat.
import java.io.IOE
import java.io.PrintW
import java.io.StringW
import java.sql.T
import java.text.DateF
import java.text.SimpleDateF
import java.util.HashM
import java.util.L
import javax.servlet.ServletC
import javax.servlet.ServletE
import javax.servlet.http.HttpS
import javax.servlet.http.HttpServletR
import javax.servlet.http.HttpServletR
import org.codehaus.jackson.map.ObjectM
import org.jivesoftware.admin.AuthCheckF
import org.jivesoftware.util.ParamU
import com.hoo.openfire.chat.logs.entity.ChatL
* &b&function:&/b& 聊天记录插件对外接口
* @author hoojo
* @createDate
下午09:32:21
* @file ChatLogsServlet.java
* @package com.hoo.openfire.chat.logs
* @project OpenfirePlugin
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@
* @version 1.0
public class ChatLogsServlet extends HttpServlet {
private static final long serialVersionUID = 7161005L;
private static final DateFormat df = new SimpleDateFormat(&yyyy-MM-dd&);
private static final ObjectMapper mapper = new ObjectMapper();
private static DbChatLogsManager logsM
public void init(ServletConfig config) throws ServletException {
super.init(config);
logsManager = DbChatLogsManager.getInstance();
// 取消权限验证,不登陆即可访问
AuthCheckFilter.addExclude(&chatlogs&);
AuthCheckFilter.addExclude(&chatlogs/ChatLogsServlet&);
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
response.setCharacterEncoding(&utf-8&);
PrintWriter out = response.getWriter();
ChatLogs entity = new ChatLogs();
String action = ParamUtils.getParameter(request, &action&);
if (&last!contact&.equals(action)) {
String sender = ParamUtils.getParameter(request, &sender&);
entity.setSender(sender);
List&String& result = logsManager.findLastContact(entity);
request.setAttribute(&lastContact&, result);
request.getRequestDispatcher(&/plugins/chatlogs/chatLogs-last-contact-service.jsp&).forward(request, response);
} else if (&all!contact&.equals(action)) {
List&String& result = logsManager.findAllContact();
request.setAttribute(&allContact&, result);
request.getRequestDispatcher(&/plugins/chatlogs/chatLogs-all-contact-service.jsp&).forward(request, response);
} else if (&remove!contact&.equals(action)) {
Integer messageId = ParamUtils.getIntParameter(request, &messageId&, -1);
logsManager.remove(messageId);
request.getRequestDispatcher(&/plugins/chatlogs/chatLogs-service.jsp&).forward(request, response);
} else if (&lately!contact&.equals(action)) {
String sender = ParamUtils.getParameter(request, &sender&);
entity.setSender(sender);
List&String& result = logsManager.findLastContact(entity);
StringWriter writer = new StringWriter();
mapper.writeValue(writer, result);
replyMessage(writer.toString(), response, out);
} else if (&entire!contact&.equals(action)) {
List&String& result = logsManager.findAllContact();
StringWriter writer = new StringWriter();
mapper.writeValue(writer, result);
replyMessage(writer.toString(), response, out);
} else if (&delete!contact&.equals(action)) {
Integer messageId = ParamUtils.getIntParameter(request, &messageId&, -1);
StringWriter writer = new StringWriter();
mapper.writeValue(writer, logsManager.remove(messageId));
replyMessage(writer.toString(), response, out);
} else if (&query&.equals(action)) {
String sender = ParamUtils.getParameter(request, &sender&);
String receiver = ParamUtils.getParameter(request, &receiver&);
String content = ParamUtils.getParameter(request, &content&);
String createDate = ParamUtils.getParameter(request, &createDate&);
if (createDate != null && !&&.equals(createDate)) {
entity.setCreateDate(new Timestamp(df.parse(createDate).getTime()));
} catch (Exception e) {
entity.setContent(content);
entity.setReceiver(receiver);
entity.setSender(sender);
List&ChatLogs& logs = logsManager.query(entity);
StringWriter writer = new StringWriter();
mapper.writeValue(writer, logs);
replyMessage(writer.toString(), response, out);
String sender = ParamUtils.getParameter(request, &sender&);
String receiver = ParamUtils.getParameter(request, &receiver&);
String content = ParamUtils.getParameter(request, &content&);
String createDate = ParamUtils.getParameter(request, &createDate&);
if (createDate != null && !&&.equals(createDate)) {
entity.setCreateDate(new Timestamp(df.parse(createDate).getTime()));
} catch (Exception e) {
entity.setContent(content);
entity.setReceiver(receiver);
entity.setSender(sender);
List&HashMap&String, Object&& logs = logsManager.search(entity);
StringWriter writer = new StringWriter();
mapper.writeValue(writer, logs);
replyMessage(writer.toString(), response, out);
public void destroy() {
super.destroy();
// Release the excluded URL
AuthCheckFilter.removeExclude(&chatlogs/ChatLogsServlet&);
AuthCheckFilter.removeExclude(&chatlogs&);
private void replyMessage(String message, HttpServletResponse response, PrintWriter out) {
response.setContentType(&text/json&);
out.println(message);
out.flush();
这个类就是个普通的Servlet,做过JavaEE应该都比较了解了。更多关注的还是在servlet初始化的时候,将插件的配置目录忽略(即白名单)不通过URL资源权限验证,也就是用户不登陆也可以反问这个资源。
注意:这个类中使用到了jackson-all-1.6.2.jar这个包,你需要把这个jar包添加到当前项目的lib目录中,并且添加到构建路径中。添加到lib中后,在build.xml脚本运行的时候就不会编译错误了。
5、 写了Servlet当然少不了配置文件,在src/plugins/chatlogs根目录下新建一个web/WEB-INF目录,在web目录中新建一个web-custom.xml文件,内容如下
&?xmlversion=&1.0&encoding=&ISO-8859-1&?&
&!DOCTYPEweb-appPUBLIC&-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN&&/dtd/web-app_2_3.dtd&&
&servlet-name&ChatLogsServlet&/servlet-name&
&servlet-class&com.hoo.openfire.chat.logs.ChatLogsServlet&/servlet-class&
&/servlet&
&servlet-mapping&
&servlet-name&ChatLogsServlet&/servlet-name&
&url-pattern&/ChatLogsServlet&/url-pattern&
&/servlet-mapping&
&/web-app&
这里我配置的是/ChatLogsServlet,但是我在后面调用的时候是无法调用到的。;但如果我用就可以调用到ChatLogsServlet接口。之前在上一篇文章我也提到了这个问题。
6、 接下来就是完成页面的编写,刚才在上面的plugin.xml文件中有一个adminconsole管理员控制台的配置。其中&item id=&chatLogs-service& name=&ChatLogs Service& url=&chatLogs-service.jsp&这个配置是最重要的,在上一篇文章中一提到,这里再说下。Item元素的id对象页面中的&meta name=&pageID& content=&chatLogs-service&/&这里的content的内容;item的url指向页面的路径名称。
chatLogs-service.jsp显示所有聊天记录、查询聊天记录、删除聊天记录
&%@page import=&com.hoo.openfire.chat.logs.entity.ChatLogs&%&
&%@page import=&java.text.SimpleDateFormat&%&
&%@page import=&java.text.DateFormat&%&
&%@page import=&java.sql.Timestamp&%&
&%@page import=&org.jivesoftware.util.ParamUtils&%&
&%@page import=&org.jivesoftware.openfire.XMPPServer&%&
&%@page import=&com.hoo.openfire.chat.logs.ChatLogsPlugin&%&
&%@page import=&com.hoo.openfire.chat.logs.DbChatLogsManager&%&
&%@ page language=&java& import=&java.util.*& pageEncoding=&UTF-8&%&
&!DOCTYPE HTML PUBLIC &-//W3C//DTD HTML 4.01 Transitional//EN&&
&title&ChatLogs 聊天记录 openfire plugin&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&meta name=&pageID& content=&chatLogs-service&/&
//ChatLogsPlugin plugin = (ChatLogsPlugin) XMPPServer.getInstance().getPluginManager().getPlugin(&chatlogs&);
String sender = ParamUtils.getParameter(request, &sender&);
String receiver = ParamUtils.getParameter(request, &receiver&);
String content = ParamUtils.getParameter(request, &content&);
String createDate = ParamUtils.getParameter(request, &createDate&);
ChatLogs entity = new ChatLogs();
DateFormat df = new SimpleDateFormat(&yyyy-MM-dd&);
entity.setCreateDate(new Timestamp(df.parse(createDate).getTime()));
} catch (Exception e) {
entity.setContent(content);
entity.setReceiver(receiver);
entity.setSender(sender);
DbChatLogsManager logsManager = DbChatLogsManager.getInstance();
List&ChatLogs& logs = logsManager.query(entity);
System.out.println(logsManager);
System.out.println(logs);
&div class=&jive-contentBoxHeader&&所有聊天用户&/div&
&div class=&jive-contentBox&&
&a href=&${pageContext.request.contextPath }/plugins/chatlogs?action=all!contact&&查看&/a&
&div class=&jive-contentBoxHeader&&搜索&/div&
&div class=&jive-contentBox&&
&form action=&chatLogs-service.jsp&&
发送人:&input type=&text& name=&sender& value=&${param.sender }&&
接收人:&input type=&text& name=&receiver& value=&${param.receiver }&&
内容:&input type=&text& name=&content& value=&${param.content }&&
发送时间:&input type=&text& name=&createDate& value=&${param.createDate }&&
&input type=&submit&&
&input type=&reset&&
&div class=&jive-table&&
&table cellpadding=&0& cellspacing=&0& border=&0& width=&100%&&
&th&发送人&/th&
&th&接收者&/th&
&th&内容&/th&
&th&发送时间&/th&
&th&删除&/th&
&% for (int i = 0, len = logs.size(); i & i++) {
ChatLogs log = logs.get(i);
&tr class=&jive-&%= i % 2 == 0 ? &even& : &odd& %&&&
&td&&%=log.getSender() %&&/td&
&td&&%=log.getReceiver() %&&/td&
&td&&%=log.getContent() %&&/td&
&td&&%=log.getCreateDate() %&&/td&
&td&&a href=&${pageContext.request.contextPath }/plugins/chatlogs?action=remove!contact&messageId=&%=log.getMessageId() %&&&
&img title=&点击删除& src=&images/delete-16x16.gif&&
chatLogs-all-contact-service.jsp显示查询所有聊天联系人
&%@page import=&org.xmpp.packet.JID&%&
&%@ page language=&java& import=&java.util.*& pageEncoding=&UTF-8&%&
&!DOCTYPE HTML PUBLIC &-//W3C//DTD HTML 4.01 Transitional//EN&&
&title&ChatLogs 聊天记录 openfire plugin&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&meta name=&pageID& content=&chatLogs-all-contact-service&/&
&div class=&jive-contentBoxHeader&&ChatLogs 所有聊天联系人&/div&
&div class=&jive-table&&
&table cellpadding=&0& cellspacing=&0& border=&0& width=&100%&&
&th&联系人JID&/th&
&th&他/她的聊天记录&/th&
&th&【他/她】的联系人&/th&
Object obj = request.getAttribute(&allContact&);
if (obj != null) {
List&String& allContact = (List&String&)
for (int i = 0, len = allContact.size(); i & i++) {
String contact = allContact.get(i);
JID jid = new JID(contact);
&tr class=&jive-&%= i % 2 == 0 ? &even& : &odd& %&&&
&td&&%=contact %&&/td&
&a href=&${pageContext.request.contextPath }/plugins/chatlogs/chatLogs-service.jsp?sender=&%=jid.getNode() %&&&他/她的聊天记录&/a&
&a href=&${pageContext.request.contextPath }/plugins/chatlogs?action=last!contact&sender=&%=jid.getNode() %&&&他/她的联系人&/a&
chatLogs-last-contact-service.jsp查询某个用户的最近联系人
&%@ page language=&java& import=&java.util.*& pageEncoding=&UTF-8&%&
&!DOCTYPE HTML PUBLIC &-//W3C//DTD HTML 4.01 Transitional//EN&&
&title&ChatLogs 聊天记录 openfire plugin&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&meta name=&pageID& content=&chatLogs-all-contact-service&/&
&div class=&jive-contentBoxHeader&&ChatLogs 所有聊天联系人&/div&
&div class=&jive-table&&
&table cellpadding=&0& cellspacing=&0& border=&0& width=&100%&&
&th&联系人JID&/th&
&th&【他/她】的联系人&/th&
&th&他/她的聊天记录&/th&
Object obj = request.getAttribute(&lastContact&);
if (obj != null) {
List&String& allContact = (List&String&)
for (int i = 0, len = allContact.size(); i & i++) {
String contact = allContact.get(i);
&tr class=&jive-&%= i % 2 == 0 ? &even& : &odd& %&&&
&td&&%=contact %&&/td&
&a href=&${pageContext.request.contextPath }/plugins/chatlogs/chatLogs-service.jsp?sender=${param.sender}&receiver=&%=contact%&&&和他/她的聊天记录信息&/a&
&a href=&${pageContext.request.contextPath }/plugins/chatlogs/chatLogs-service.jsp?receiver=&%=contact%&&&和他/她的聊天记录信息&/a&
OK,至此整个插件基本编写完成,下面就开始打包部署,打包是很关键的。
三、打包发布插件
1、 上次编写ant命令不能编译JSP页面有引用类的页面,这次重新提供代码。打可部署jar包。在工程的根目录中新建一个build目录,新建build.xml
&project name=&Webapp Precompilation& default=&openfire-plugins& basedir=&.&&
&property file=&build.properties& /&
&!-- java servlet相关文件编译jar存放位置 --&
&property name=&java.jar.dir& value=&${webapp.path}/java-dist&/&
&!-- jsp servlet编译后jar存放位置 --&
&property name=&jsp.jar.dir& value=&${webapp.path}/jsp-dist/lib&/&
&!-- 定义java servlet和jsp servlet的jar包名称 --&
&property name=&java.jar& value=&${java.jar.dir}/plugin-${plugin.name}.jar&/&
&property name=&jsp.jar& value=&${jsp.jar.dir}/plugin-${plugin.name}-jsp.jar&/&
&!-- jsp servlet配置到web.xml中 --&
&property name=&plugin.web.xml& value=&${webapp.path}/jsp-dist/web.xml&/&
&!-- 编译jsp 并生成相关jar、xml文件 --&
&target name=&jspc&&
&taskdef classname=&org.apache.jasper.JspC& name=&jasper2&&
&classpath id=&jspc.classpath&&
&pathelement location=&${java.home}/../lib/tools.jar& /&
&fileset dir=&${tomcat.home}/bin&&
&include name=&*.jar& /&
&/fileset&
&fileset dir=&${tomcat.home}/server/lib&&
&include name=&*.jar& /&
&/fileset&
&fileset dir=&${tomcat.home}/common/lib&&
&include name=&*.jar& /&
&/fileset&
&/classpath&
&/taskdef&
&jasper2 javaEncoding=&UTF-8& validateXml=&false&
uriroot=&${plugin.path}/web&
outputDir=&${webapp.path}/jsp-dist/src&
package=&com.hoo.openfire.plugin.${plugin.name}& /&
validateXml=&false&
uriroot=&${plugin.path}/web&
outputDir=&${webapp.path}/jsp-dist/src&
package=&com.hoo.openfire.plugin.${plugin.name}&
webXml=&${plugin.web.xml}&/&
&!-- 编译jsp 并打jar包 --&
&target name=&compile&&
&mkdir dir=&${webapp.path}/jsp-dist/classes& /&
&mkdir dir=&${webapp.path}/jsp-dist/lib& /&
&mkdir dir=&${webapp.path}/jsp-dist/src& /&
&javac destdir=&${webapp.path}/jsp-dist/classes& optimize=&off&
encoding=&UTF-8& debug=&on& failonerror=&false&
srcdir=&${webapp.path}/jsp-dist/src& excludes=&**/*.smap&&
&!-- compilerarg value=&-Xlint:unchecked&/
&compilerarg value=&-Xlint&/&--&
&classpath&
&pathelement location=&${webapp.path}/jsp-dist/classes& /&
&fileset dir=&${webapp.path}/jsp-dist/lib&&
&include name=&*.jar& /&
&/fileset&
&pathelement location=&${tomcat.home}/common/classes& /&
&fileset dir=&${tomcat.home}/common/lib&&
&include name=&*.jar& /&
&/fileset&
&pathelement location=&${tomcat.home}/shared/classes& /&
&fileset dir=&${tomcat.home}/shared/lib&&
&include name=&*.jar& /&
&/fileset&
&fileset dir=&${tomcat.home}/bin&&
&include name=&*.jar& /&
&/fileset&
&pathelement location=&${webapp.path}/bin& /&
&fileset dir=&${webapp.path}/lib&&
&include name=&**/*.jar& /&
&/fileset&
&/classpath&
&include name=&**& /&
&exclude name=&tags/**& /&
&jar jarfile=&${jsp.jar}& basedir=&${webapp.path}/jsp-dist/classes& /&
&!-- 将java servlet打包成jar --&
&target name=&java-jar&&
&mkdir dir=&${java.jar.dir}&/&
&jar jarfile=&${java.jar}&&
&fileset dir=&${webapp.path}/bin& includes=&**/*.class&/&
&!-- 生成可部署的插件包 --&
&target name=&plug-jar&&
&!-- 插件插件包相关lib、 web目录 --&
&mkdir dir=&${webapp.path}/${plugin.name}/lib&/&
&mkdir dir=&${webapp.path}/${plugin.name}/web/WEB-INF&/&
&!-- 复制jsp servlet的jar和java servlet的相关jar包到插件包的lib目录下 --&
&copy file=&${java.jar}& todir=&${webapp.path}/${plugin.name}/lib&/&
&copy file=&${jsp.jar}& todir=&${webapp.path}/${plugin.name}/lib&/&
&!-- 将相关的图片、帮助文档、修改日志等文件复制到插件目录下 --&
&copy todir=&${webapp.path}/${plugin.name}&&
&fileset dir=&${plugin.path}& includes=&*.*&/&
&copy todir=&${webapp.path}/${plugin.name}/web&&
&fileset dir=&${plugin.path}/web&&
&include name=&*&/&
&include name=&**/*.*&/&
&exclude name=&**/*.xml&/&
&exclude name=&**/*.jsp&/&
&/fileset&
&!-- jsp servlet的web复制到插件目录下 --&
&copy file=&${plugin.web.xml}& todir=&${webapp.path}/${plugin.name}/web/WEB-INF&/&
&copy todir=&${webapp.path}/${plugin.name}/web&&
&fileset dir=&${plugin.path}/web& includes=&**/*.xml&/&
&!-- 将国际化相关资源文件复制到插件目录下
&copy file=&${webapp.path}/bin/i18n& todir=&${webapp.path}/${plugin.name}&/&
&!-- 产生可部署插件包 --&
&jar jarfile=&${webapp.path}/${plugin.name}.jar&&
&fileset dir=&${webapp.path}/${plugin.name}& includes=&**/**&/&
&!-- 生成没有Web资源的可部署插件包 --&
&target name=&java-plug-jar&&
&!-- 插件插件包相关lib、 web目录 --&
&mkdir dir=&${webapp.path}/${plugin.name}/lib&/&
&!-- 复制java servlet的相关jar包到插件包的lib目录下 --&
&copy file=&${java.jar}& todir=&${webapp.path}/${plugin.name}/lib&/&
&!-- 将相关的图片、帮助文档、修改日志等文件复制到插件目录下 --&
&copy todir=&${webapp.path}/${plugin.name}&&
&fileset dir=&${plugin.path}& includes=&*.*&/&
&!-- 产生可部署插件包 --&
&jar jarfile=&${webapp.path}/${plugin.name}.jar&&
&fileset dir=&${webapp.path}/${plugin.name}& includes=&**/**&/&
&!-- 清理生成的文件 --&
&target name=&clean&&
&delete file=&${webapp.path}/${plugin.name}.jar&/&
&delete dir=&${webapp.path}/${plugin.name}&/&
&delete dir=&${webapp.path}/jsp-dist&/&
&delete dir=&${webapp.path}/java-dist&/&
&target name=&all& depends=&clean,jspc,compile&/&
&target name=&openfire-plugin& depends=&jspc,java-jar&/&
&target name=&openfire-plugins& depends=&all,java-jar,plug-jar&/&
&target name=&openfire-plugin-java& depends=&clean,java-jar,java-plug-jar&/&
&/project&
build.properties文件内容
tomcat.home=c:/SoftWare/tomcat-5.0.28/tomcat-5.0.28
webapp.path=D:/eclipse_workspace/OpenfirePlugin
plugin.name=chatLogs
plugin.path=D\:/eclipse_workspace/OpenfirePlugin/src/plugins/chatlogs
至于这里为什么需要这个配置,上一篇文章已经有讲明。如果你需要了解的话,请看上一篇博文。运行默认的build.xml脚本中的默认ant命令,就可以看到当前项目的根目录中有一个chatLogs.jar 这个就是我们编写好打好的插件包了。
2、 发布插件
在发布之前,我们还需要做一件事情。在ChatLogsServlet中我们有使用ObjectMapper这个json的工具包。我们打包可以成功,但是在ChatLogsServlet这个接口在调用的时候会出现问题。你需要把jackson-all-1.6.2.jar这个jar包放在你的openfire服务的lib目录,也就是C:\Program Files\openfire\lib这个目录中,这样整个环境中就可以使用这个jar包了。
小提示:如果你的插件发布成功,但是没有达到预期的效果。那么很有可能插件中的代码出现了异常信息。你可以进入管理员控制台的日志看到操作系统的日志信息。日志内容有常用的几个级别,经常看看日志对开发插件有帮助的。我拦截消息包的时候是分析消息包的xml内容作的判断,才完成这个插件的开发的。
发布插件:直接将插件放置在openfire服务器的plugins目录下。我的是在:C:\Program Files\openfire\plugins目录。重起openfire后你可以看到控制台输出我们插件中输出的内容,并且在C:\Program Files\openfire\plugins目录中可以看到该目录下多了一个chatlogs的目录(openfire可以自动解压jar包)。
当你在关闭服务器的瞬间,也会打印销毁插件的消息。
插件按照成功后,访问页面你就可以看到安装好的插件了。
至此,整个插件部署成功了。
3、 测试插件
我们开一个spark和一个WebIM聊天程序进行互相聊天。内容大概如下:
发送聊天内容的时候你可以看到启动的openfire控制台有一些我们打印的报文。当聊天终止后,我们可以关闭openfire服务器。然后启动HSQL数据库查看下ofChatLogs这张表的数据,如果数据保存成功。那么效果达到了,如果没有消息内容,你得看看是哪里错误或遗漏了。建议你看看的日志,可能会有提示信息。我这里查询后,大致内容如下:
OK,下面我们可以看看在 openfire管理员控制台服务器设置左侧菜单中的ChatLogs Servcie 这个就是我们的插件。点击后就可以看到上面的测试的聊天内容了,你可以输入发送者、接受者等内容进行搜索,还可以点击“所有聊天用户”查看连接,查询所有聊天用户以及他们的聊天记录信息。
随后,我们应该测试下我们编写的ChatLogsServlet对外的查询接口。在浏览器中直接请求:会返回一段JSON数据,存放所有的聊天用户的sessionJID。
[&boy@127.0.0.1/WebIM&,&girl@127.0.0.1/WebIM&,]
返回聊天记录所有结果,JSON格式的数组集合
查询hoojo和boy的聊天内容,JSON格式的数组集合
其他的还可以通过内容,发送日期进行搜索等等,基本功能就这样的。如果你想要用到自己的系统中,如果涉及到跨域,你需要用到Proxy、URLConnection、HttpClient等网络编程的方法来解决跨域问题。
【】 手把手教你配置Openfire服务器
【】教你一步步开发自己的插件
【】 优美清新的界面,可以多窗口聊天
【】 可以基于他开发Java的聊天应用
【】 多人聊天室,如果结合Smack和Openfire,就可以实现外网聊天应用
【】 本地应用,不需要Openfire服务器
【】理论知识,便于连接Openfire
【】 拓展你的应用,可以了解开源的jwchat,全JS的应用
【】 移动手机和Openfire的整合方案
【】 DWR实现聊天应用,简单快速
阅读(...) 评论()}

我要回帖

更多关于 聊天技巧 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信