您的位置:首页 > 运维架构

基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件[转]

2014-11-14 08:58 381 查看

上一篇文章介绍到怎么在自己的Java环境中搭建openfire插件开发的环境,同时介绍到怎样一步步简单的开发openfire插件。一步步很详细的介绍到简单插件开发,带Servlet的插件的开发、带JSP页面插件的开发,以及怎么样将开发好的插件打包、部署到openfire服务器。

如果你没有看上一篇文章的话,请你还是看看。/article/4791935.html

因为这篇文章是基于上篇文章讲叙的基础上完成插件开发。而且开发的环境及打包的ant脚本都是共用的。如果你在看这篇文章有什么不好理解的地方,还请麻烦你自己再去翻阅之前的文章。这样对你可能有更多的帮助!

同样在这篇文章开始之前,如果你不知道怎么使用openfire,安装openfire服务器,建议你看以下文章:

/article/4791916.html

/article/4791915.html




开发环境:

System:Windows

WebBrowser:IE6+、Firefox3+

JavaEEServer:tomcat5.0.2.8、tomcat6

IDE:eclipse、MyEclipse6.5



开发依赖库:

Jdk1.6、jasper-compiler.jar、jasper-runtime.jar、openfire.jar、servlet.jar

Email:hoojo_@126.com

Blog:http://blog.csdn.net/IBM_hoojo

http://hoojo.cnblogs.com/



如果你觉得这篇文章不错或对你有帮助的话,请你支持我。如果觉这里的文章不错的话,请你关注我的博客。

推荐文章:

Openfire的安装和配置】手把手教你配置Openfire服务器

跟我一步一步开发自己的Openfire插件】教你一步步开发自己的插件

JavaScript/jQuery、HTML、CSS构建WebIM远程及时聊天通信程序】优美清新的界面,可以多窗口聊天

Smack结合Openfire服务器,建立IM通信,发送聊天消息】可以基于他开发Java的聊天应用

ApacheMiNa实现多人聊天室】多人聊天室,如果结合Smack和Openfire,就可以实现外网聊天应用

JavaScript/jQueryWebIM及时聊天通信工具本地客户端】本地应用,不需要Openfire服务器

Openfire与XMPP协议】理论知识,便于连接Openfire

Jwchat的安装和配置、Serviceunavailable、Authorizationfailed问题汇总】拓展你的应用,可以了解开源的jwchat,全JS的应用

移动应用(手机应用)开发IM聊天程序解决方案】移动手机和Openfire的整合方案

Spring整合DWRcomet实现无刷新多人聊天室】DWR实现聊天应用,简单快速




我把自己写好的插件打包,下载后部署到openfire服务器,就可以用了。如果出现什么问题的话,你可以看看这篇文章,都有解决方法。

插件下载:http://files.cnblogs.com/hoojo/%E8%81%8A%E5%A4%A9%E6%8F%92%E4%BB%B6.rar




基本原理(流程)







一、准备工作


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、执行你的聊天记录数据库脚本,聊天记录表内容如下

hsqldb

[code]
[code]--Createtable--openfire聊天记录

createtableOFCHATLOGS

(

MESSAGEIDintprimarykey,--消息id

SESSIONJIDVARCHAR(30),--用户sessionjid名称

SENDERVARCHAR(30),--消息发送者

RECEIVERVARCHAR(30),--接受者

CREATEDATEVARCHAR(30),--消息发送、创建时间

LENGTHint,--消息长度、大小

CONTENTVARCHAR(2000),--消息内容

DETAILVARCHAR(4000),--消息源报文

STATEint--删除状态,1表示删除

);

[/code]
[/code]

Oracledb

[code]
[code]--Createtable

createtableOFCHATLOGS

(

MESSAGEIDintnotnull,

SESSIONJIDNVARCHAR2(30),

SENDERNVARCHAR2(30),

RECEIVERNVARCHAR2(30),

CREATEDATENVARCHAR2(30),--TIMESTAMP(12),

LENGTHint,

CONTENTNVARCHAR2(2000),

DETAILNVARCHAR2(4000),

STATEint

);

--Addcommentstothetable

commentontableOFCHATLOGS

is'openfire聊天记录';

--Addcommentstothecolumns

commentoncolumnOFCHATLOGS.MESSAGEID

is'消息id';

commentoncolumnOFCHATLOGS.SESSIONJID

is'用户sessionjid名称';

commentoncolumnOFCHATLOGS.SENDER

is'消息发送者';

commentoncolumnOFCHATLOGS.RECEIVER

is'接受者';

commentoncolumnOFCHATLOGS.CREATEDATE

is'消息发送、创建时间';

commentoncolumnOFCHATLOGS.LENGTH

is'消息长度、大小';

commentoncolumnOFCHATLOGS.CONTENT

is'消息内容';

commentoncolumnOFCHATLOGS.DETAIL

is'消息源报文';

commentoncolumnOFCHATLOGS.STATE

is'删除状态,1表示删除';

--Create/Recreateprimary,uniqueandforeignkeyconstraints

AltertableOFCHATLOGS

AddconstraintPKMESSAGEIDprimarykey(MESSAGEID);

[/code]
[/code]
注意:如果你是oracle数据库,执行上面的脚本可能没有什么问题。如果你是使用openfire默认的数据库hsqldb。那么你可能不知道在哪里运行hql脚本。不要急,跟着我做!这些都是小菜一碟的事情。

下面的内容是openfire默认数据库的脚本和数据库的使用方法,不是用openfire默认数据库的“攻城师”可以跳过。

3.1进入到你的openfire安装目录C:\ProgramFiles\openfire\bin\extra,在该目录下你可以看到





这个就是数据库启动的dos程序,如果你是Linux的系统。那当然是运行embedded-db-viewer.sh这个。如果你的windows的系统,就运行embedded-db-viewer.bat程序。

注意:在启动数据库前,请保证你的openfire服务器没有启动。要不然你是无法启动的。因为你启动了openfire服务器的话,数据库已经在使用了,Openfire会锁定数据库的。因为openfire不希望在读写数据的时候,有人在干预它,导致存在脏读,重写的情况。

3.2启动后就可以看到如下界面





在空白区域你可以写你的SQL脚本,写完后点击ExcuteSQL就可以运行。执行完成后,在右下方区域可以看到结果。

Ok,你现在就可以将hsql的聊天记录表的脚本放在这里执行。数据库表就可以创建成功了。创建成功后你可以看到下面输出updatecount0。在左边可以看到OFCHATLOGS这个table。

创建好数据库表后,下面我们就开发自己的openfire聊天插件核心代码。




4、在开始核心代码之前,我们需要建立一个简单的JavaEntity实体对象。在src/plugins/chatlogs目录中建立com.hoo.openfire.chat.logs.entity包,在包下建立文件

[code]packagecom.hoo.openfire.chat.logs.entity;


importjava.sql.Timestamp;

importorg.jivesoftware.util.JiveConstants;


/**

*<b>function:</b>聊天记录对象实体

*@authorhoojo

*@createDate2012-9-19下午08:28:03

*@fileChatLogs.java

*@packagecom.hoo.openfire.chat.logs.entity

*@projectOpenfirePlugin

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassChatLogs{


privatelongmessageId;

privateStringsessionJID;

privateStringsender;

privateStringreceiver;

privateTimestampcreateDate;

privateStringcontent;

privateStringdetail;

privateintlength;

privateintstate;//1表示删除


publicinterfaceLogState{

intshow=0;

intremove=1;

}


/**

*<b>function:</b>自增id序列管理器,类型变量

*@authorhoojo

*@createDate2012-9-20下午02:38:52

*@fileChatLogs.java

*@packagecom.hoo.openfire.chat.logs.entity

*@projectOpenfirePlugin

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassChatLogsConstantsextendsJiveConstants{

//日志表id自增对应类型

publicstaticfinalintCHAT_LOGS=52;

//用户在线统计id自增对应类型

publicstaticfinalintUSER_ONLINE_STATE=53;

}


publicChatLogs(){

}


publicChatLogs(StringsessionJID,TimestampcreateDate,Stringcontent,Stringdetail,intlength){

super();

this.sessionJID=sessionJID;

this.createDate=createDate;

this.content=content;

this.detail=detail;

this.length=length;

}


publicChatLogs(longmessageId,StringsessionJID,TimestampcreateDate,Stringcontent,Stringdetail,intlength,intstate){

super();

this.messageId=messageId;

this.sessionJID=sessionJID;

this.createDate=createDate;

this.content=content;

this.detail=detail;

this.length=length;

this.state=state;

}


//setter/getter

}

[/code]


二、开发聊天记录插件


按照上面给出的工程的目录结构,新建我们需要的文件。

1、在src/plugins/chatlogs目录新建包com.hoo.openfire.chat.logs,在包中建立DbChatLogsManager聊天记录CRUD数据库操作类


[code]packagecom.hoo.openfire.chat.logs;


importjava.sql.Connection;

importjava.sql.PreparedStatement;

importjava.sql.ResultSet;

importjava.sql.ResultSetMetaData;

importjava.sql.SQLException;

importjava.sql.Statement;

importjava.text.DateFormat;

importjava.text.SimpleDateFormat;

importjava.util.ArrayList;

importjava.util.Date;

importjava.util.HashMap;

importjava.util.List;

importorg.apache.commons.lang.StringUtils;

importorg.jivesoftware.database.DbConnectionManager;

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;


importcom.hoo.openfire.chat.logs.entity.ChatLogs;


/**

*<b>function:</b>聊天记录db操作类

*@authorhoojo

*@createDate2012-9-19下午04:15:43

*@fileDbChatLogsManager.java

*@packagecom.iflashbuy.openfire.chat.logs

*@projectOpenfirePlugin

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassDbChatLogsManager{


privatestaticfinalLoggerLog=LoggerFactory.getLogger(DbChatLogsManager.class);

privatestaticfinalDbChatLogsManagerCHAT_LOGS_MANAGER=newDbChatLogsManager();


privateDbChatLogsManager(){

}


publicstaticDbChatLogsManagergetInstance(){

returnCHAT_LOGS_MANAGER;

}


privatestaticfinalStringLOGS_COUNT="SELECTcount(1)FROMofChatLogs";

privatestaticfinalStringLOGS_LAST_MESSAGE_ID="SELECTmax(messageId)FROMofChatLogs";

privatestaticfinalStringLOGS_FIND_BY_ID="SELECTmessageId,sessionJID,sender,receiver,createDate,length,contentFROMofChatLogswheremessageId=?";

privatestaticfinalStringLOGS_REMOVE="UPDATEofChatLogssetstate=1wheremessageId=?";//"DELETEFROMofChatLogsWHEREmessageId=?";

privatestaticfinalStringLOGS_INSERT="INSERTINTOofChatLogs(messageId,sessionJID,sender,receiver,createDate,length,content,detail,state)VALUES(?,?,?,?,?,?,?,?,?)";

privatestaticfinalStringLOGS_QUERY="SELECTmessageId,sessionJID,sender,receiver,createDate,length,contentFROMofChatLogswherestate=0";

privatestaticfinalStringLOGS_SEARCH="SELECT*FROMofChatLogswherestate=0";

privatestaticfinalStringLOGS_LAST_CONTACT="SELECTdistinctreceiverFROMofChatLogswherestate=0andsender=?";

privatestaticfinalStringLOGS_ALL_CONTACT="SELECTdistinctsessionJIDFROMofChatLogswherestate=0";


/**

*<b>function:</b>获取最后一个id

*@authorhoojo

*@createDate2012-9-19下午08:13:33

*@return最后一个记录id

*/

publicintgetLastId(){

Connectioncon=null;

PreparedStatementpstmt=null;

ResultSetrs=null;

intcount=-1;

try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_LAST_MESSAGE_ID);

rs=pstmt.executeQuery();

if(rs.next()){

count=rs.getInt(1);

}else{

count=0;

}

}catch(SQLExceptionsqle){

Log.error(sqle.getMessage(),sqle);

return0;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

returncount;

}


/**

*<b>function:</b>获取总数量

*@authorhoojo

*@createDate2012-9-19下午08:14:59

*@return总数量

*/

publicintgetCount(){

Connectioncon=null;

PreparedStatementpstmt=null;

ResultSetrs=null;

intcount=-1;

try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_COUNT);

rs=pstmt.executeQuery();

if(rs.next()){

count=rs.getInt(1);

}else{

count=0;

}

}catch(SQLExceptionsqle){

Log.error(sqle.getMessage(),sqle);

return0;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

returncount;

}


/**

*<b>function:</b>删除聊天记录信息

*@authorhoojo

*@createDate2012-9-19下午08:25:48

*@paramid聊天信息id

*@return

*/

publicbooleanremove(Integerid){

Connectioncon=null;

PreparedStatementpstmt=null;

try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_REMOVE);

pstmt.setInt(1,id);

returnpstmt.execute();


}catch(SQLExceptionsqle){

Log.error("chatLogsremoveexception:{}",sqle);

returnfalse;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}


/**

*<b>function:</b>添加聊天记录信息

*@authorhoojo

*@createDate2012-9-19下午08:37:52

*@paramlogsChatLogs聊天记录对象

*@return是否添加成功

*/

publicbooleanadd(ChatLogslogs){

Connectioncon=null;

PreparedStatementpstmt=null;

try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_INSERT);

inti=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());


returnpstmt.execute();

}catch(SQLExceptionsqle){

Log.error("chatLogsaddexception:{}",sqle);

returnfalse;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}


/**

*<b>function:</b>通过id查询聊天记录信息

*@authorhoojo

*@createDate2012-9-19下午09:32:19

*@paramid消息id

*@returnChatLogs

*/

publicChatLogsfind(intid){

Connectioncon=null;

PreparedStatementpstmt=null;

ChatLogslogs=null;


try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_FIND_BY_ID);

pstmt.setInt(1,id);

ResultSetrs=pstmt.executeQuery();

while(rs.next()){

logs=newChatLogs();

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"));

}

returnlogs;

}catch(SQLExceptionsqle){

Log.error("chatLogsfindexception:{}",sqle);

returnlogs;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}


/**

*<b>function:</b>多条件搜索查询,返回List<ChatLogs>集合

*@authorhoojo

*@createDate2012-9-19下午09:34:45

*@paramentityChatLogs

*@return返回List<ChatLogs>集合

*/

publicList<ChatLogs>query(ChatLogsentity){

Connectioncon=null;

Statementpstmt=null;

ChatLogslogs=null;


List<ChatLogs>result=newArrayList<ChatLogs>();


try{

con=DbConnectionManager.getConnection();

pstmt=con.createStatement();


Stringsql=LOGS_QUERY;

if(entity!=null){

if(!StringUtils.isEmpty(entity.getSender())&&!StringUtils.isEmpty(entity.getReceiver())){

sql+="and(sender='"+entity.getSender()+"'andreceiver='"+entity.getReceiver()+"')";

sql+="or(receiver='"+entity.getSender()+"'andsender='"+entity.getReceiver()+"')";


}else{

if(!StringUtils.isEmpty(entity.getSender())){

sql+="andsender='"+entity.getSender()+"'";

}

if(!StringUtils.isEmpty(entity.getReceiver())){

sql+="andreceiver='"+entity.getReceiver()+"'";

}

}



if(!StringUtils.isEmpty(entity.getContent())){

sql+="andcontentlike'%"+entity.getContent()+"%'";

}

if(entity.getCreateDate()!=null){

DateFormatdf=newSimpleDateFormat("yyyy-MM-dd");

StringcrateatDate=df.format(newDate(entity.getCreateDate().getTime()));

//sql+="andto_char(createDate,'yyyy-mm-dd')='"+crateatDate+"'";

sql+="andcreateDatelike'"+crateatDate+"%'";

}

}

sql+="orderbycreateDateasc";

ResultSetrs=pstmt.executeQuery(sql);

while(rs.next()){

logs=newChatLogs();

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);

}

returnresult;

}catch(SQLExceptionsqle){

Log.error("chatLogssearchexception:{}",sqle);

returnresult;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}


/**

*<b>function:</b>多条件搜索查询,返回List<Map>集合

*@authorhoojo

*@createDate2012-9-19下午09:33:28

*@paramentityChatLogs

*@returnList<HashMap<String,Object>>

*/

publicList<HashMap<String,Object>>search(ChatLogsentity){

Connectioncon=null;

Statementpstmt=null;


List<HashMap<String,Object>>result=newArrayList<HashMap<String,Object>>();


try{

con=DbConnectionManager.getConnection();

pstmt=con.createStatement();


Stringsql=LOGS_SEARCH;

if(entity!=null){

if(!StringUtils.isEmpty(entity.getSender())&&!StringUtils.isEmpty(entity.getReceiver())){

sql+="and(sender='"+entity.getSender()+"'andreceiver='"+entity.getReceiver()+"')";

sql+="or(receiver='"+entity.getSender()+"'andsender='"+entity.getReceiver()+"')";


}else{

if(!StringUtils.isEmpty(entity.getSender())){

sql+="andsender='"+entity.getSender()+"'";

}

if(!StringUtils.isEmpty(entity.getReceiver())){

sql+="andreceiver='"+entity.getReceiver()+"'";

}

}

if(!StringUtils.isEmpty(entity.getContent())){

sql+="andcontentlike'%"+entity.getContent()+"%'";

}

if(entity.getCreateDate()!=null){

DateFormatdf=newSimpleDateFormat("yyyy-MM-dd");

StringcrateatDate=df.format(newDate(entity.getCreateDate().getTime()));

sql+="andto_char(createDate,'yyyy-mm-dd')='"+crateatDate+"'";

}

}

sql+="orderbycreateDateasc";

ResultSetrs=pstmt.executeQuery(sql);


ResultSetMetaDatarsmd=rs.getMetaData();

/**获取结果集的列数*/

intcolumnCount=rsmd.getColumnCount();

while(rs.next()){

HashMap<String,Object>map=newHashMap<String,Object>();

/**把每一行以(key,value)存入HashMap,列名做为key,列值做为value*/

for(inti=1;i<=columnCount;++i){

StringcolumnVal=rs.getString(i);

if(columnVal==null){

columnVal="";

}

map.put(rsmd.getColumnName(i),columnVal);

}

/**把装有一行数据的HashMap存入list*/

result.add(map);

}

returnresult;

}catch(SQLExceptionsqle){

Log.error("chatLogssearchexception:{}",sqle);

returnresult;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}


/**

*<b>function:</b>最近联系人

*@authorhoojo

*@createDate2013-3-24下午4:38:51

*@paramentity聊天记录实体

*@return最近联系人集合

*/

publicList<String>findLastContact(ChatLogsentity){

Connectioncon=null;

PreparedStatementpstmt=null;

List<String>result=newArrayList<String>();


try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_LAST_CONTACT);

pstmt.setString(1,entity.getSender());

ResultSetrs=pstmt.executeQuery();

while(rs.next()){

result.add(rs.getString("receiver"));

}

returnresult;

}catch(SQLExceptionsqle){

Log.error("chatLogsfindexception:{}",sqle);

returnresult;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}


/**

*<b>function:</b>查找所有聊天用户

*@authorhoojo

*@createDate2013-3-24下午4:37:40

*@return所有聊天用户sessionJID集合

*/

publicList<String>findAllContact(){

Connectioncon=null;

PreparedStatementpstmt=null;

List<String>result=newArrayList<String>();


try{

con=DbConnectionManager.getConnection();

pstmt=con.prepareStatement(LOGS_ALL_CONTACT);

ResultSetrs=pstmt.executeQuery();

while(rs.next()){

result.add(rs.getString("sessionJID"));

}

returnresult;

}catch(SQLExceptionsqle){

Log.error("chatLogsfindexception:{}",sqle);

returnresult;

}finally{

DbConnectionManager.closeConnection(pstmt,con);

}

}

}

[/code]

比较简单,都是数据库的增删改查的JDBC操作。就是打开数据库连接和关闭数据库连接是使用openfire提供的DbConnectionManager类完成的。




2、插件核心类,也就是保存聊天记录的类。这里对PacketInterceptor、Plugin进行继承。如果开发插件就一定要继承Plugin,而继承PacketInterceptor是拦截用户发送的消息包。对消息包进行过滤、拦截,保存我们需要的数据。openfire的插件可以访问所有openfire的API。这给我们的插件实现提供了巨大的灵活性。以下提供了四种比较常用的插件集成方式。

2.1、Component:可以接收一个特定子域(sub-domain)的所有包。比如test_componet.hoo.com。所以一个发送给jojo@test_componet.hoo.com的包将被转发给这个componet.

2.2、IQHandler:相应包中特定的元素名或命名空间。下面的代码展示了如何注册一个IQHandler.

IQHandlermyHandler=newMyIQHander();

IQRouteriqRouter=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类,插件核心类代码如下


[code]packagecom.hoo.openfire.chat.logs;


importjava.io.File;

importjava.sql.Timestamp;

importjava.util.Date;

importjava.util.List;

importorg.jivesoftware.database.SequenceManager;

importorg.jivesoftware.openfire.XMPPServer;

importorg.jivesoftware.openfire.container.Plugin;

importorg.jivesoftware.openfire.container.PluginManager;

importorg.jivesoftware.openfire.interceptor.InterceptorManager;

importorg.jivesoftware.openfire.interceptor.PacketInterceptor;

importorg.jivesoftware.openfire.interceptor.PacketRejectedException;

importorg.jivesoftware.openfire.session.Session;

importorg.jivesoftware.openfire.user.UserManager;

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

importorg.xmpp.packet.IQ;

importorg.xmpp.packet.JID;

importorg.xmpp.packet.Message;

importorg.xmpp.packet.Packet;

importorg.xmpp.packet.Presence;

importcom.hoo.openfire.chat.logs.entity.ChatLogs;

importcom.hoo.openfire.chat.logs.entity.ChatLogs.ChatLogsConstants;


/**

*<b>function:</b>聊天记录插件

*@authorhoojo

*@createDate2012-9-19下午01:47:20

*@fileChatLogsPacketInterceptor.java

*@packagecom.hoo.openfire.chat.logs

*@projectOpenfirePlugin

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassChatLogsPluginimplementsPacketInterceptor,Plugin{


privatestaticfinalLoggerlog=LoggerFactory.getLogger(ChatLogsPlugin.class);


privatestaticPluginManagerpluginManager;

privatestaticDbChatLogsManagerlogsManager;


publicChatLogsPlugin(){

interceptorManager=InterceptorManager.getInstance();

logsManager=DbChatLogsManager.getInstance();

}


//Hookforintercpetorn

privateInterceptorManagerinterceptorManager;


/**

*<b>function:</b>拦截消息核心方法,Packet就是拦截消息对象

*@authorhoojo

*@createDate2013-3-27下午04:49:11

*/

@Override

publicvoidinterceptPacket(Packetpacket,Sessionsession,booleanincoming,booleanprocessed)throwsPacketRejectedException{

if(session!=null){

debug(packet,incoming,processed,session);

}


JIDrecipient=packet.getTo();

if(recipient!=null){

Stringusername=recipient.getNode();

//广播消息或是不存在/没注册的用户.

if(username==null||!UserManager.getInstance().isRegisteredUser(recipient)){

return;

}elseif(!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())){

//非当前openfire服务器信息

return;

}elseif("".equals(recipient.getResource())){

}

}

this.doAction(packet,incoming,processed,session);

}


/**

*<b>function:</b>执行保存/分析聊天记录动作

*@authorhoojo

*@createDate2013-3-24下午12:20:56

*@parampacket数据包

*@paramincomingtrue表示发送方

*@paramsession当前用户session

*/

privatevoiddoAction(Packetpacket,booleanincoming,booleanprocessed,Sessionsession){

PacketcopyPacket=packet.createCopy();

if(packetinstanceofMessage){

Messagemessage=(Message)copyPacket;


//一对一聊天,单人模式

if(message.getType()==Message.Type.chat){

log.info("单人聊天信息:{}",message.toXML());

debug("单人聊天信息:"+message.toXML());


//程序执行中;是否为结束或返回状态(是否是当前session用户发送消息)

if(processed||!incoming){

return;

}

logsManager.add(this.get(packet,incoming,session));


//群聊天,多人模式

}elseif(message.getType()==Message.Type.groupchat){

List<?>els=message.getElement().elements("x");

if(els!=null&&!els.isEmpty()){

log.info("群聊天信息:{}",message.toXML());

debug("群聊天信息:"+message.toXML());

}else{

log.info("群系统信息:{}",message.toXML());

debug("群系统信息:"+message.toXML());

}


//其他信息

}else{

log.info("其他信息:{}",message.toXML());

debug("其他信息:"+message.toXML());

}

}elseif(packetinstanceofIQ){

IQiq=(IQ)copyPacket;

if(iq.getType()==IQ.Type.set&&iq.getChildElement()!=null&&"session".equals(iq.getChildElement().getName())){

log.info("用户登录成功:{}",iq.toXML());

debug("用户登录成功:"+iq.toXML());

}

}elseif(packetinstanceofPresence){

Presencepresence=(Presence)copyPacket;

if(presence.getType()==Presence.Type.unavailable){

log.info("用户退出服务器成功:{}",presence.toXML());

debug("用户退出服务器成功:"+presence.toXML());

}

}

}


/**

*<b>function:</b>创建一个聊天记录实体对象,并设置相关数据

*@authorhoojo

*@createDate2013-3-27下午04:44:54

*@parampacket数据包

*@paramincoming如果为ture就表明是发送者

*@paramsession当前用户session

*@return聊天实体

*/

privateChatLogsget(Packetpacket,booleanincoming,Sessionsession){

Messagemessage=(Message)packet;

ChatLogslogs=newChatLogs();


JIDjid=session.getAddress();

if(incoming){//发送者

logs.setSender(jid.getNode());

JIDrecipient=message.getTo();

logs.setReceiver(recipient.getNode());

}

logs.setContent(message.getBody());

logs.setCreateDate(newTimestamp(newDate().getTime()));

logs.setDetail(message.toXML());

logs.setLength(logs.getContent().length());

logs.setState(0);

logs.setSessionJID(jid.toString());

//生成主键id,利用序列生成器

longmessageID=SequenceManager.nextID(ChatLogsConstants.CHAT_LOGS);

logs.setMessageId(messageID);


returnlogs;

}


/**

*<b>function:</b>调试信息

*@authorhoojo

*@createDate2013-3-27下午04:44:31

*@parampacket数据包

*@paramincoming如果为ture就表明是发送者

*@paramprocessed执行

*@paramsession当前用户session

*/

privatevoiddebug(Packetpacket,booleanincoming,booleanprocessed,Sessionsession){

Stringinfo="[packetID:"+packet.getID()+",to:"+packet.getTo()+",from:"+packet.getFrom()+",incoming:"+incoming+",processed:"+processed+"]";


longtimed=System.currentTimeMillis();

debug("###################start###################"+timed);

debug("id:"+session.getStreamID()+",address:"+session.getAddress());

debug("info:"+info);

debug("xml:"+packet.toXML());

debug("###################end#####################"+timed);


log.info("id:"+session.getStreamID()+",address:"+session.getAddress());

log.info("info:{}",info);

log.info("pluginName:"+pluginManager.getName(this)+",xml:"+packet.toXML());

}


privatevoiddebug(Objectmessage){

if(true){

System.out.println(message);

}

}


@Override

publicvoiddestroyPlugin(){

interceptorManager.removeInterceptor(this);

debug("销毁聊天记录插件成功!");

}


@Override

publicvoidinitializePlugin(PluginManagermanager,FilepluginDirectory){

interceptorManager.addInterceptor(this);

pluginManager=manager;


debug("安装聊天记录插件成功!");

}

}

[/code]

注意在初始化插件的时候,在系统的烂机器管理器中添加对当前插件对象的管理,即在interceptorManager中addInterceptor。而在销毁资源的时候则removeInterceptor当前对象。

上面已经在打印出用户聊天的Packet信息,当用户登陆、退出、发送消息等,都会被拦截到。我们只需要拦截我们要的消息数据,注意下面看这段代码


[code]//程序执行中;是否为结束或返回状态(是否是当前session用户发送消息)

if(processed||!incoming){

return;

}

[/code]

如果没有这段代码,那我们就可以保存很多重复或无用的信息。为什么这样写,看看在控制台或日子infohttp://127.0.0.1:9090/logviewer.jsp?log=info中的信息:

一个用户hoojo和离线用户boy聊天Packet内容


[code]###################start###################1364442703632

id:172af964,address:hoojo@127.0.0.1/Spark2.6.3

info:[packetID:4O1WO-29,to:boy@127.0.0.1,from:hoojo@127.0.0.1/Spark2.6.3,incoming:true,processed:false]

xml:<messageid="4O1WO-29"to="boy@127.0.0.1"from="hoojo@127.0.0.1/Spark2.6.3"type="chat"><body>哈哈,我上线了~~</body><thread>yOgoRq</thread><xxmlns="jabber:x:event"><offline/><composing/></x></message>

###################end#####################1364442703632


###################start###################1364442703659

id:172af964,address:hoojo@127.0.0.1/Spark2.6.3

info:[packetID:4O1WO-29,to:boy@127.0.0.1,from:hoojo@127.0.0.1/Spark2.6.3,incoming:true,processed:true]

xml:<messageid="4O1WO-29"to="boy@127.0.0.1"from="hoojo@127.0.0.1/Spark2.6.3"type="chat"><body>哈哈,我上线了~~</body><thread>yOgoRq</thread><xxmlns="jabber:x:event"><offline/><composing/></x></message>

###################end#####################1364442703659

[/code]

主要看incoming和processed的值:incoming都为ture,incoming为true就表示是自己发送的信息。而procesed为false,然后才是true,processed为true就表示发送结束。且session都是当前聊天用户。

hoojo@127.0.0.1/Spark2.6.3incoming:true,processed:false;

-->hoojo@127.0.0.1/Spark2.6.3incoming:true,processed:true;

通过这个状态,我们的判断代码应该可以拦截到的是第一种状态。然后就可以将一种状态的Packet保存到聊天记录表中,其他的数据暂不处理!

用户hoojo和一个在线用户boy聊天Packet


[code]###################start###################1364443976157

id:172af964,address:hoojo@127.0.0.1/Spark2.6.3

info:[packetID:4O1WO-30,to:boy@127.0.0.1,from:hoojo@127.0.0.1/Spark2.6.3,incoming:true,processed:false]

xml:<messageid="4O1WO-30"to="boy@127.0.0.1"from="hoojo@127.0.0.1/Spark2.6.3"type="chat"><body>看状态……</body><thread>yOgoRq</thread><xxmlns="jabber:x:event"><offline/><composing/></x></message>

###################end#####################1364443976157

###################start###################1364443976223

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/Spark2.6.3,incoming:false,processed:false]

xml:<messageid="4O1WO-30"to="boy@127.0.0.1"from="hoojo@127.0.0.1/Spark2.6.3"type="chat"><body>看状态……</body><thread>yOgoRq</thread><xxmlns="jabber:x:event"><offline/><composing/></x></message>

###################end#####################1364443976223

###################start###################1364443976228

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/Spark2.6.3,incoming:false,processed:true]

xml:<messageid="4O1WO-30"to="boy@127.0.0.1"from="hoojo@127.0.0.1/Spark2.6.3"type="chat"><body>看状态……</body><thread>yOgoRq</thread><xxmlns="jabber:x:event"><offline/><composing/></x></message>

###################end#####################1364443976228

###################start###################1364443976232

id:172af964,address:hoojo@127.0.0.1/Spark2.6.3

info:[packetID:4O1WO-30,to:boy@127.0.0.1,from:hoojo@127.0.0.1/Spark2.6.3,incoming:true,processed:true]

xml:<messageid="4O1WO-30"to="boy@127.0.0.1"from="hoojo@127.0.0.1/Spark2.6.3"type="chat"><body>看状态……</body><thread>yOgoRq</thread><xxmlns="jabber:x:event"><offline/><composing/></x></message>

###################end#####################1364443976232

[/code]

状态流程:

hoojo@127.0.0.1/Spark2.6.3incoming:true,processed:false;

-->boy@127.0.0.1/WebIMincoming:false,processed:false;

-->boy@127.0.0.1/WebIMincoming:false,processed:true;

-->hoojo@127.0.0.1/Spark2.6.3incoming:true,processed:true;

而我们保存消息的状态是在第一个状态,即incoming=true,processed=false的这个状态保存的。

看图,这样更利于理解





离线用户的流程就是没有红色部分的,其他用户就是整体的流程部分。




3、为ChatLogsPlugin添加配置,在src/plugins/chatlogs目录下建立plugin.xml


[code]<?xmlversion="1.0"encoding="UTF-8"?>

<plugin>

<class>com.hoo.openfire.chat.logs.ChatLogsPlugin</class>


<!--Pluginmeta-data-->

<name>ChatLogsPlugin</name>

<description>UserChatMessageLogsPlugins.</description>

<author>hoojo[http://hoojo.cnblogs.com]</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>

<tabid="tab-server">

<sidebarid="sidebar-server-settings">

<itemid="chatLogs-service"name="ChatLogsService"url="chatLogs-service.jsp"

description="Clicktomanagetheservicethatallowsuserschatlogs."/>

</sidebar>

</tab>

</adminconsole>

</plugin>

[/code]

如果你看过上一篇文章,这个配置你就不陌生了。最主要的还是class这个配置。在上面的配置中有一个adminconsole配置是页面的配置,暂且忽略。稍后再提,ok!

至此插件的核心部分已经配置完成了,如果你现在打包部署的话,肯定是可以保存到数据库。如果你想试试的话,可以按照上一篇文章的步骤进行打包部署。打包不带jsp的ant命令-java-plug-jar即可。这里我就不运行这步,继续完成其他的操作演示。




4、在src/plugins/chatlogs目录中的com.hoo.openfire.chat.logs包下新建ChatLogsServlet,提供对外调用的接口,一般用它查询聊天记录和联系人信息


[code]packagecom.hoo.openfire.chat.logs;


importjava.io.IOException;

importjava.io.PrintWriter;

importjava.io.StringWriter;

importjava.sql.Timestamp;

importjava.text.DateFormat;

importjava.text.SimpleDateFormat;

importjava.util.HashMap;

importjava.util.List;


importjavax.servlet.ServletConfig;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServlet;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;


importorg.codehaus.jackson.map.ObjectMapper;

importorg.jivesoftware.admin.AuthCheckFilter;

importorg.jivesoftware.util.ParamUtils;


importcom.hoo.openfire.chat.logs.entity.ChatLogs;


/**

*<b>function:</b>聊天记录插件对外接口

*

*@authorhoojo

*@createDate2012-9-18下午09:32:21

*@fileChatLogsServlet.java

*@packagecom.hoo.openfire.chat.logs

*@projectOpenfirePlugin

*@blog'target='_blank'>http://blog.csdn.net/IBM_hoojo[/code]
*@emailhoojo_@126.com

*@version1.0

*/

publicclassChatLogsServletextendsHttpServlet{


privatestaticfinallongserialVersionUID=6981863134047161005L;

privatestaticfinalDateFormatdf=newSimpleDateFormat("yyyy-MM-dd");

privatestaticfinalObjectMappermapper=newObjectMapper();

privatestaticDbChatLogsManagerlogsManager;


@Override

publicvoidinit(ServletConfigconfig)throwsServletException{

super.init(config);


logsManager=DbChatLogsManager.getInstance();


//取消权限验证,不登陆即可访问

AuthCheckFilter.addExclude("chatlogs");

AuthCheckFilter.addExclude("chatlogs/ChatLogsServlet");

}


@Override

protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{

doPost(request,response);

}


@Override

protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,

IOException{

response.setCharacterEncoding("utf-8");

PrintWriterout=response.getWriter();


ChatLogsentity=newChatLogs();

Stringaction=ParamUtils.getParameter(request,"action");


if("last!contact".equals(action)){

Stringsender=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);


}elseif("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);


}elseif("remove!contact".equals(action)){

IntegermessageId=ParamUtils.getIntParameter(request,"messageId",-1);

logsManager.remove(messageId);


request.getRequestDispatcher("/plugins/chatlogs/chatLogs-service.jsp").forward(request,response);


}elseif("lately!contact".equals(action)){

Stringsender=ParamUtils.getParameter(request,"sender");

entity.setSender(sender);


List<String>result=logsManager.findLastContact(entity);

StringWriterwriter=newStringWriter();

mapper.writeValue(writer,result);

replyMessage(writer.toString(),response,out);


}elseif("entire!contact".equals(action)){

List<String>result=logsManager.findAllContact();

StringWriterwriter=newStringWriter();

mapper.writeValue(writer,result);

replyMessage(writer.toString(),response,out);


}elseif("delete!contact".equals(action)){

IntegermessageId=ParamUtils.getIntParameter(request,"messageId",-1);


StringWriterwriter=newStringWriter();

mapper.writeValue(writer,logsManager.remove(messageId));

replyMessage(writer.toString(),response,out);


}elseif("query".equals(action)){


Stringsender=ParamUtils.getParameter(request,"sender");

Stringreceiver=ParamUtils.getParameter(request,"receiver");

Stringcontent=ParamUtils.getParameter(request,"content");

StringcreateDate=ParamUtils.getParameter(request,"createDate");


try{

if(createDate!=null&&!"".equals(createDate)){

entity.setCreateDate(newTimestamp(df.parse(createDate).getTime()));

}

}catch(Exceptione){

}

entity.setContent(content);

entity.setReceiver(receiver);

entity.setSender(sender);


List<ChatLogs>logs=logsManager.query(entity);


StringWriterwriter=newStringWriter();

mapper.writeValue(writer,logs);

replyMessage(writer.toString(),response,out);

}else{

Stringsender=ParamUtils.getParameter(request,"sender");

Stringreceiver=ParamUtils.getParameter(request,"receiver");

Stringcontent=ParamUtils.getParameter(request,"content");

StringcreateDate=ParamUtils.getParameter(request,"createDate");


try{

if(createDate!=null&&!"".equals(createDate)){

entity.setCreateDate(newTimestamp(df.parse(createDate).getTime()));

}

}catch(Exceptione){

}

entity.setContent(content);

entity.setReceiver(receiver);

entity.setSender(sender);


List<HashMap<String,Object>>logs=logsManager.search(entity);

StringWriterwriter=newStringWriter();

mapper.writeValue(writer,logs);

replyMessage(writer.toString(),response,out);

}

}


@Override

publicvoiddestroy(){

super.destroy();


//ReleasetheexcludedURL

AuthCheckFilter.removeExclude("chatlogs/ChatLogsServlet");

AuthCheckFilter.removeExclude("chatlogs");

}


privatevoidreplyMessage(Stringmessage,HttpServletResponseresponse,PrintWriterout){

response.setContentType("text/json");

out.println(message);

out.flush();

}

}

[/code]

这个类就是个普通的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文件,内容如下

[code]<?xmlversion="1.0"encoding="ISO-8859-1"?>

<!DOCTYPEweb-appPUBLIC"-//SunMicrosystems,Inc.//DTDWebApplication2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>


<servlet>

<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>

[/code]

这里我配置的是/ChatLogsServlet,但是我在后面调用的时候是无法调用到的。http://127.0.0.1:9090/plugins/chatlogs/ChatLogsServlet?action=query;但如果我用http://127.0.0.1:9090/plugins/chatlogs?action=query就可以调用到ChatLogsServlet接口。之前在上一篇文章我也提到了这个问题。




6、接下来就是完成页面的编写,刚才在上面的plugin.xml文件中有一个adminconsole管理员控制台的配置。其中<itemid="chatLogs-service"name="ChatLogsService"url="chatLogs-service.jsp"这个配置是最重要的,在上一篇文章中一提到,这里再说下。Item元素的id对象页面中的<metaname="pageID"content="chatLogs-service"/>这里的content的内容;item的url指向页面的路径名称。

chatLogs-service.jsp显示所有聊天记录、查询聊天记录、删除聊天记录


[code]<%@pageimport="com.hoo.openfire.chat.logs.entity.ChatLogs"%>

<%@pageimport="java.text.SimpleDateFormat"%>

<%@pageimport="java.text.DateFormat"%>

<%@pageimport="java.sql.Timestamp"%>

<%@pageimport="org.jivesoftware.util.ParamUtils"%>

<%@pageimport="org.jivesoftware.openfire.XMPPServer"%>

<%@pageimport="com.hoo.openfire.chat.logs.ChatLogsPlugin"%>

<%@pageimport="com.hoo.openfire.chat.logs.DbChatLogsManager"%>

<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>

<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">

<html>

<head>

<title>ChatLogs聊天记录openfireplugin</title>

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<metaname="pageID"content="chatLogs-service"/>

</head>


<%

//ChatLogsPluginplugin=(ChatLogsPlugin)XMPPServer.getInstance().getPluginManager().getPlugin("chatlogs");

Stringsender=ParamUtils.getParameter(request,"sender");

Stringreceiver=ParamUtils.getParameter(request,"receiver");

Stringcontent=ParamUtils.getParameter(request,"content");

StringcreateDate=ParamUtils.getParameter(request,"createDate");


ChatLogsentity=newChatLogs();

DateFormatdf=newSimpleDateFormat("yyyy-MM-dd");

try{

entity.setCreateDate(newTimestamp(df.parse(createDate).getTime()));

}catch(Exceptione){

}

entity.setContent(content);

entity.setReceiver(receiver);

entity.setSender(sender);


DbChatLogsManagerlogsManager=DbChatLogsManager.getInstance();

List<ChatLogs>logs=logsManager.query(entity);


System.out.println(logsManager);

System.out.println(logs);

%>


<body>

<divclass="jive-contentBoxHeader">所有聊天用户</div>

<divclass="jive-contentBox">

<ahref="${pageContext.request.contextPath}/plugins/chatlogs?action=all!contact">查看</a>

</div>


<divclass="jive-contentBoxHeader">搜索</div>

<divclass="jive-contentBox">

<formaction="chatLogs-service.jsp">

发送人:<inputtype="text"name="sender"value="${param.sender}">

接收人:<inputtype="text"name="receiver"value="${param.receiver}">

内容:<inputtype="text"name="content"value="${param.content}">

发送时间:<inputtype="text"name="createDate"value="${param.createDate}">


<inputtype="submit">

<inputtype="reset">

</form>

</div>


<divclass="jive-table">

<tablecellpadding="0"cellspacing="0"border="0"width="100%">

<thead>

<tr>

<th>发送人</th>

<th>接收者</th>

<th>内容</th>

<th>发送时间</th>

<th>删除</th>

</tr>

</thead>

<tbody>

<%for(inti=0,len=logs.size();i<len;i++){

ChatLogslog=logs.get(i);

%>

<trclass="jive-<%=i%2==0?"even":"odd"%>">

<td><%=log.getSender()%></td>

<td><%=log.getReceiver()%></td>

<td><%=log.getContent()%></td>

<td><%=log.getCreateDate()%></td>

<td><ahref="${pageContext.request.contextPath}/plugins/chatlogs?action=remove!contact&messageId=<%=log.getMessageId()%>">

<imgtitle="点击删除"src="images/delete-16x16.gif">

</a></td>

</tr>

<%}%>

</tbody>

</table>

</div>

</body>

</html>

[/code]

chatLogs-all-contact-service.jsp显示查询所有聊天联系人


[code]<%@pageimport="org.xmpp.packet.JID"%>

<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>

<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">

<html>

<head>

<title>ChatLogs聊天记录openfireplugin</title>

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<metaname="pageID"content="chatLogs-all-contact-service"/>

</head>


<body>

<divclass="jive-contentBoxHeader">ChatLogs所有聊天联系人</div>


<divclass="jive-table">

<tablecellpadding="0"cellspacing="0"border="0"width="100%">

<thead>

<tr>

<th>联系人JID</th>

<th>他/她的聊天记录</th>

<th>【他/她】的联系人</th>

</tr>

</thead>

<tbody>

<%

Objectobj=request.getAttribute("allContact");

if(obj!=null){

List<String>allContact=(List<String>)obj;

for(inti=0,len=allContact.size();i<len;i++){

Stringcontact=allContact.get(i);

JIDjid=newJID(contact);

%>

<trclass="jive-<%=i%2==0?"even":"odd"%>">

<td><%=contact%></td>

<td>

<ahref="${pageContext.request.contextPath}/plugins/chatlogs/chatLogs-service.jsp?sender=<%=jid.getNode()%>">他/她的聊天记录</a>

</td>

<td>

<ahref="${pageContext.request.contextPath}/plugins/chatlogs?action=last!contact&sender=<%=jid.getNode()%>">他/她的联系人</a>

</td>

</tr>

<%}

}

%>

</tbody>

</table>

</div>

</body>

</html>

[/code]

chatLogs-last-contact-service.jsp查询某个用户的最近联系人


[code]<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>

<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">

<html>

<head>

<title>ChatLogs聊天记录openfireplugin</title>

<metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">

<metaname="pageID"content="chatLogs-all-contact-service"/>

</head>


<body>

<divclass="jive-contentBoxHeader">ChatLogs所有聊天联系人</div>


<divclass="jive-table">

<tablecellpadding="0"cellspacing="0"border="0"width="100%">

<thead>

<tr>

<th>联系人JID</th>

<th>【他/她】的联系人</th>

<th>他/她的聊天记录</th>

</tr>

</thead>

<tbody>

<%

Objectobj=request.getAttribute("lastContact");

if(obj!=null){

List<String>allContact=(List<String>)obj;

for(inti=0,len=allContact.size();i<len;i++){

Stringcontact=allContact.get(i);

%>

<trclass="jive-<%=i%2==0?"even":"odd"%>">

<td><%=contact%></td>

<td>

<ahref="${pageContext.request.contextPath}/plugins/chatlogs/chatLogs-service.jsp?sender=${param.sender}&receiver=<%=contact%>">和他/她的聊天记录信息</a>

</td>

<td>

<ahref="${pageContext.request.contextPath}/plugins/chatlogs/chatLogs-service.jsp?receiver=<%=contact%>">和他/她的聊天记录信息</a>

</td>

</tr>

<%}

}

%>

</tbody>

</table>

</div>

</body>

</html>

[/code]

OK,至此整个插件基本编写完成,下面就开始打包部署,打包是很关键的。



三、打包发布插件


1、上次编写ant命令不能编译JSP页面有引用类的页面,这次重新提供代码。打可部署jar包。在工程的根目录中新建一个build目录,新建build.xml


[code]<projectname="WebappPrecompilation"default="openfire-plugins"basedir=".">

<propertyfile="build.properties"/>


<!--javaservlet相关文件编译jar存放位置-->

<propertyname="java.jar.dir"value="${webapp.path}/java-dist"/>

<!--jspservlet编译后jar存放位置-->

<propertyname="jsp.jar.dir"value="${webapp.path}/jsp-dist/lib"/>


<!--定义javaservlet和jspservlet的jar包名称-->

<propertyname="java.jar"value="${java.jar.dir}/plugin-${plugin.name}.jar"/>

<propertyname="jsp.jar"value="${jsp.jar.dir}/plugin-${plugin.name}-jsp.jar"/>


<!--jspservlet配置到web.xml中-->

<propertyname="plugin.web.xml"value="${webapp.path}/jsp-dist/web.xml"/>


<!--编译jsp并生成相关jar、xml文件-->

<targetname="jspc">


<taskdefclassname="org.apache.jasper.JspC"name="jasper2">

<classpathid="jspc.classpath">

<pathelementlocation="${java.home}/../lib/tools.jar"/>

<filesetdir="${tomcat.home}/bin">

<includename="*.jar"/>

</fileset>

<filesetdir="${tomcat.home}/server/lib">

<includename="*.jar"/>

</fileset>

<filesetdir="${tomcat.home}/common/lib">

<includename="*.jar"/>

</fileset>

</classpath>

</taskdef>


<jasper2javaEncoding="UTF-8"validateXml="false"

uriroot="${plugin.path}/web"

outputDir="${webapp.path}/jsp-dist/src"

package="com.hoo.openfire.plugin.${plugin.name}"/>


<jasper2

validateXml="false"

uriroot="${plugin.path}/web"

outputDir="${webapp.path}/jsp-dist/src"

package="com.hoo.openfire.plugin.${plugin.name}"

webXml="${plugin.web.xml}"/>

</target>


<!--编译jsp并打jar包-->

<targetname="compile">


<mkdirdir="${webapp.path}/jsp-dist/classes"/>

<mkdirdir="${webapp.path}/jsp-dist/lib"/>

<mkdirdir="${webapp.path}/jsp-dist/src"/>


<javacdestdir="${webapp.path}/jsp-dist/classes"optimize="off"

encoding="UTF-8"debug="on"failonerror="false"

srcdir="${webapp.path}/jsp-dist/src"excludes="**/*.smap">

<!--compilerargvalue="-Xlint:unchecked"/

<compilerargvalue="-Xlint"/>-->

<classpath>

<pathelementlocation="${webapp.path}/jsp-dist/classes"/>

<filesetdir="${webapp.path}/jsp-dist/lib">

<includename="*.jar"/>

</fileset>

<pathelementlocation="${tomcat.home}/common/classes"/>

<filesetdir="${tomcat.home}/common/lib">

<includename="*.jar"/>

</fileset>

<pathelementlocation="${tomcat.home}/shared/classes"/>

<filesetdir="${tomcat.home}/shared/lib">

<includename="*.jar"/>

</fileset>

<filesetdir="${tomcat.home}/bin">

<includename="*.jar"/>

</fileset>


<pathelementlocation="${webapp.path}/bin"/>

<filesetdir="${webapp.path}/lib">

<includename="**/*.jar"/>

</fileset>

</classpath>

<includename="**"/>

<excludename="tags/**"/>

</javac>


<jarjarfile="${jsp.jar}"basedir="${webapp.path}/jsp-dist/classes"/>

</target>


<!--将javaservlet打包成jar-->

<targetname="java-jar">

<mkdirdir="${java.jar.dir}"/>

<jarjarfile="${java.jar}">

<filesetdir="${webapp.path}/bin"includes="**/*.class"/>

</jar>

</target>


<!--生成可部署的插件包-->

<targetname="plug-jar">

<!--插件插件包相关lib、web目录-->

<mkdirdir="${webapp.path}/${plugin.name}/lib"/>

<mkdirdir="${webapp.path}/${plugin.name}/web/WEB-INF"/>


<!--复制jspservlet的jar和javaservlet的相关jar包到插件包的lib目录下-->

<copyfile="${java.jar}"todir="${webapp.path}/${plugin.name}/lib"/>

<copyfile="${jsp.jar}"todir="${webapp.path}/${plugin.name}/lib"/>


<!--将相关的图片、帮助文档、修改日志等文件复制到插件目录下-->

<copytodir="${webapp.path}/${plugin.name}">

<filesetdir="${plugin.path}"includes="*.*"/>

</copy>

<copytodir="${webapp.path}/${plugin.name}/web">

<filesetdir="${plugin.path}/web">

<includename="*"/>

<includename="**/*.*"/>

<excludename="**/*.xml"/>

<excludename="**/*.jsp"/>

</fileset>

</copy>

<!--jspservlet的web复制到插件目录下-->

<copyfile="${plugin.web.xml}"todir="${webapp.path}/${plugin.name}/web/WEB-INF"/>

<copytodir="${webapp.path}/${plugin.name}/web">

<filesetdir="${plugin.path}/web"includes="**/*.xml"/>

</copy>

<!--将国际化相关资源文件复制到插件目录下

<copyfile="${webapp.path}/bin/i18n"todir="${webapp.path}/${plugin.name}"/>

-->

<!--产生可部署插件包-->

<jarjarfile="${webapp.path}/${plugin.name}.jar">

<filesetdir="${webapp.path}/${plugin.name}"includes="**/**"/>

</jar>

</target>


<!--生成没有Web资源的可部署插件包-->

<targetname="java-plug-jar">

<!--插件插件包相关lib、web目录-->

<mkdirdir="${webapp.path}/${plugin.name}/lib"/>


<!--复制javaservlet的相关jar包到插件包的lib目录下-->

<copyfile="${java.jar}"todir="${webapp.path}/${plugin.name}/lib"/>


<!--将相关的图片、帮助文档、修改日志等文件复制到插件目录下-->

<copytodir="${webapp.path}/${plugin.name}">

<filesetdir="${plugin.path}"includes="*.*"/>

</copy>


<!--产生可部署插件包-->

<jarjarfile="${webapp.path}/${plugin.name}.jar">

<filesetdir="${webapp.path}/${plugin.name}"includes="**/**"/>

</jar>

</target>


<!--清理生成的文件-->

<targetname="clean">

<deletefile="${webapp.path}/${plugin.name}.jar"/>

<deletedir="${webapp.path}/${plugin.name}"/>

<deletedir="${webapp.path}/jsp-dist"/>

<deletedir="${webapp.path}/java-dist"/>

</target>


<targetname="all"depends="clean,jspc,compile"/>


<targetname="openfire-plugin"depends="jspc,java-jar"/>


<targetname="openfire-plugins"depends="all,java-jar,plug-jar"/>


<targetname="openfire-plugin-java"depends="clean,java-jar,java-plug-jar"/>

</project>

[/code]

build.properties文件内容

[code]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

[/code]

至于这里为什么需要这个配置,上一篇文章已经有讲明。如果你需要了解的话,请看上一篇博文。运行默认的build.xml脚本中的默认ant命令,就可以看到当前项目的根目录中有一个chatLogs.jar这个就是我们编写好打好的插件包了。




2、发布插件

在发布之前,我们还需要做一件事情。在ChatLogsServlet中我们有使用ObjectMapper这个json的工具包。我们打包可以成功,但是在ChatLogsServlet这个接口在调用的时候会出现问题。你需要把jackson-all-1.6.2.jar这个jar包放在你的openfire服务的lib目录,也就是C:\ProgramFiles\openfire\lib这个目录中,这样整个环境中就可以使用这个jar包了。

小提示:如果你的插件发布成功,但是没有达到预期的效果。那么很有可能插件中的代码出现了异常信息。你可以进入管理员控制台的日志http://127.0.0.1:9090/logviewer.jsp看到操作系统的日志信息。日志内容有常用的几个级别,经常看看日志对开发插件有帮助的。我拦截消息包的时候是分析消息包的xml内容作的判断,才完成这个插件的开发的。

发布插件:直接将插件放置在openfire服务器的plugins目录下。我的是在:C:\ProgramFiles\openfire\plugins目录。重起openfire后你可以看到控制台输出我们插件中输出的内容,并且在C:\ProgramFiles\openfire\plugins目录中可以看到该目录下多了一个chatlogs的目录(openfire可以自动解压jar包)。





当你在关闭服务器的瞬间,也会打印销毁插件的消息。

插件按照成功后,访问http://localhost:9090/plugin-admin.jsp页面你就可以看到安装好的插件了。





至此,整个插件部署成功了。




3、测试插件

我们开一个spark和一个WebIM聊天程序进行互相聊天。内容大概如下:









发送聊天内容的时候你可以看到启动的openfire控制台有一些我们打印的报文。当聊天终止后,我们可以关闭openfire服务器。然后启动HSQL数据库查看下ofChatLogs这张表的数据,如果数据保存成功。那么效果达到了,如果没有消息内容,你得看看是哪里错误或遗漏了。建议你看看http://127.0.0.1:9090/logviewer.jsp的日志,可能会有提示信息。我这里查询后,大致内容如下:





OK,下面我们可以看看在openfire管理员控制台服务器设置左侧菜单中的ChatLogsServciehttp://127.0.0.1:9090/profile-settings.jsp这个就是我们的插件。点击后就可以看到上面的测试的聊天内容了,你可以输入发送者、接受者等内容进行搜索,还可以点击“所有聊天用户”查看连接,查询所有聊天用户以及他们的聊天记录信息。

随后,我们应该测试下我们编写的ChatLogsServlet对外的查询接口。在浏览器中直接请求:http://127.0.0.1:9090/plugins/chatlogs?action=entire!contact会返回一段JSON数据,存放所有的聊天用户的sessionJID。

["boy@127.0.0.1/WebIM","girl@127.0.0.1/WebIM",hoojo@127.0.0.1/Spark2.6.3]

请求:http://127.0.0.1:9090/plugins/chatlogs?action=last!contact&sender=boy

[hoojo@127.0.0.1/Spark2.6.3]

请求:http://127.0.0.1:9090/plugins/chatlogs?action=query

返回聊天记录所有结果,JSON格式的数组集合


请求:http://127.0.0.1:9090/plugins/chatlogs?action=query&sender=hoojo&receiver=boy

查询hoojo和boy的聊天内容,JSON格式的数组集合

其他的还可以通过内容,发送日期进行搜索等等,基本功能就这样的。如果你想要用到自己的系统中,如果涉及到跨域,你需要用到Proxy、URLConnection、HttpClient等网络编程的方法来解决跨域问题。




推荐文章:

Openfire的安装和配置】手把手教你配置Openfire服务器

跟我一步一步开发自己的Openfire插件】教你一步步开发自己的插件

JavaScript/jQuery、HTML、CSS构建WebIM远程及时聊天通信程序】优美清新的界面,可以多窗口聊天

Smack结合Openfire服务器,建立IM通信,发送聊天消息】可以基于他开发Java的聊天应用

ApacheMiNa实现多人聊天室】多人聊天室,如果结合Smack和Openfire,就可以实现外网聊天应用

JavaScript/jQueryWebIM及时聊天通信工具本地客户端】本地应用,不需要Openfire服务器

Openfire与XMPP协议】理论知识,便于连接Openfire

Jwchat的安装和配置、Serviceunavailable、Authorizationfailed问题汇总】拓展你的应用,可以了解开源的jwchat,全JS的应用

移动应用(手机应用)开发IM聊天程序解决方案】移动手机和Openfire的整合方案

Spring整合DWRcomet实现无刷新多人聊天室】DWR实现聊天应用,简单快速

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: