轩辕传奇官网聊天记录保存在哪儿?

更多频道内容在这里查看
爱奇艺用户将能永久保存播放记录
过滤短视频
暂无长视频(电视剧、纪录片、动漫、综艺、电影)播放记录,
使用您的微博帐号登录,即刻尊享微博用户专属服务。
使用您的QQ帐号登录,即刻尊享QQ用户专属服务。
使用您的人人帐号登录,即刻尊享人人用户专属服务。
方式2:免费发送短信到手机
请输入正确的手机号
此短信不收取任何费用,保证您的手机号安全不被泄露
下载APP到本机
当前浏览器仅支持手动复制代码
视频地址:
flash地址:
html代码:
收藏成功,可进入查看所有收藏列表
30秒后自动关闭
色情低俗内容
血腥暴力内容
广告或欺诈内容
侵犯了我的权力
还可以输入300字
阿里旺旺如何设置让电脑自动保存聊天记录
播放量数据:
&正在加载...
阿里旺旺如何将全部设置在服务器中保存阿里旺旺如何将全部设置在服务器中保存阿里旺旺如何将全部设置在服..
教育风云榜
爱奇艺独播
由于版权原因,此视频暂不支持播放
Sorry,due to copyright issues
you can’t watch the video
这些视频最近很火,赶紧看看吧
很抱歉,您所观看的视频已经下线
这些视频最近很火,赶紧看看吧
呃......网络出错了
您使用浏览器不支持直接复制的功能,建议您使用Ctrl+C或右键全选进行地址复制
&li data-elem="tabtitle" data-seq="{{seq}}"&
&a href="javascript:void(0);"&
&span>{{start}}-{{end}}&/span&
&li data-downloadSelect-elem="item" data-downloadSelect-selected="false" data-downloadSelect-tvid="{{tvid}}"&
&a href="javascript:void(0);"&{{pd}}&/a&
选择您要下载的《》剧集:
您使用浏览器不支持直接复制的功能,建议您使用Ctrl+C或右键全选进行地址复制
安装爱奇艺视频客户端,
马上开始为您下载本片
5秒后自动消失
&li data-elem="tabtitle" data-seq="{{seq}}"&
&a href="javascript:void(0);"&
&span>{{start}}-{{end}}&/span&
&li data-downloadSelect-elem="item" data-downloadSelect-selected="false" data-downloadSelect-tvid="{{tvid}}"&
&a href="javascript:void(0);"&{{pd}}&/a&
选择您要下载的《淘宝阿里旺旺聊天的设置》剧集:评论-1032&
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实现聊天应用,简单快速
阅读(...) 评论()

我要回帖

更多关于 轩辕传奇灵殇碎片 的文章

 

随机推荐