'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实现聊天应用,简单快速
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理