基於開源 Openfire 聊天服務器 - 開發Openfire聊天記錄插件


上一篇文章介紹到怎么在自己的Java環境中搭建openfire插件開發的環境,同時介紹到怎樣一步步簡單的開發openfire插件。一步步很詳細的介紹到簡單插件開發,帶Servlet的插件的開發、帶JSP頁面插件的開發,以及怎么樣將開發好的插件打包、部署到openfire服務器。

 

如果你沒有看上一篇文章的話,請你還是看看。http://www.cnblogs.com/hoojo/archive/2013/03/07/2947502.html

因為這篇文章是基於上篇文章講敘的基礎上完成插件開發。而且開發的環境及打包的ant腳本都是共用的。如果你在看這篇文章有什么不好理解的地方,還請麻煩你自己再去翻閱之前的文章。這樣對你可能有更多的幫助!

 

同樣在這篇文章開始之前,如果你不知道怎么使用openfire,安裝openfire服務器,建議你看以下文章:

http://www.cnblogs.com/hoojo/archive/2012/05/17/2506769.html

http://www.cnblogs.com/hoojo/archive/2012/05/13/2498151.html

 

開發環境:

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_@126.com

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

http://hoojo.cnblogs.com/

如果你覺得這篇文章不錯或對你有幫助的話,請你支持我。如果覺這里的文章不錯的話,請你關注我的博客。

 

推薦文章:

Openfire 的安裝和配置】 手把手教你配置Openfire服務器

跟我一步一步開發自己的Openfire插件】教你一步步開發自己的插件

JavaScript/jQuery、HTML、CSS 構建 Web IM 遠程及時聊天通信程序】 優美清新的界面,可以多窗口聊天

Smack 結合 Openfire服務器,建立IM通信,發送聊天消息】 可以基於他開發Java的聊天應用

Apache MiNa 實現多人聊天室】 多人聊天室,如果結合Smack和Openfire,就可以實現外網聊天應用

JavaScript/jQuery WebIM 及時聊天通信工具 本地客戶端】 本地應用,不需要Openfire服務器

Openfire與XMPP協議】理論知識,便於連接Openfire

Jwchat 的安裝和配置、Service unavailable、Authorization failed問題匯總】 拓展你的應用,可以了解開源的jwchat,全JS的應用

移動應用(手機應用)開發IM聊天程序解決方案】 移動手機和Openfire的整合方案

Spring整合DWR comet 實現無刷新 多人聊天室】 DWR實現聊天應用,簡單快速

 

我把自己寫好的插件打包,下載后部署到openfire服務器,就可以用了。如果出現什么問題的話,你可以看看這篇文章,都有解決方法。

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

 

基本原理(流程)

aa

 

一、准備工作

1、 這里的開發環境就是上一篇文章的開發環境,如果你還沒有配置好環境或不知道怎么樣配置。那么煩請你按照上一篇博文的講述方法配置好開發環境,然后跟着我一步步開發聊天記錄插件。

2、 基於之前講的,現在在環境中創建好插件的目錄結構。新建一個plugins/chatlogs目錄,新插件的目錄文件如下,里面的部分文件是通用的,只有在src/plugins/chatlogs目錄中文件才是這次新增的文件。這次的插件目錄結構會按照下面的結構來。

clip_image002

先熟悉下上面的目錄,就簡單大致介紹下上面的目錄。在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、 執行你的聊天記錄數據庫腳本,聊天記錄表內容如下

hsql db

-- Create table --openfire聊天記錄
createtable OFCHATLOGS
(
  MESSAGEID  int primary key,    --消息id
  SESSIONJID VARCHAR(30),        --用戶session jid名稱
  SENDER    VARCHAR(30),         --消息發送者
  RECEIVER     VARCHAR(30),        --接受者    
  CREATEDATE VARCHAR(30),        --消息發送、創建時間
  LENGTH    int,                 --消息長度、大小
  CONTENT    VARCHAR(2000),        --消息內容
  DETAIL     VARCHAR(4000),        --消息源報文
  STATE         int                --刪除狀態,1表示刪除
);

 

Oracle db

-- Create table
createtable OFCHATLOGS
(
  MESSAGEID  int not null,
  SESSIONJID NVARCHAR2(30),
  SENDER     NVARCHAR2(30),
  RECEIVER      NVARCHAR2(30),
  CREATEDATE NVARCHAR2(30),--TIMESTAMP(12),
  LENGTH     int,
  CONTENT    NVARCHAR2(2000),
  DETAIL     NVARCHAR2(4000),
  STATE         int
);
-- 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,在該目錄下你可以看到

clip_image004

這個就是數據庫啟動的dos程序,如果你是Linux的系統。那當然是運行embedded-db-viewer.sh這個。如果你的windows的系統,就運行embedded-db-viewer.bat程序。

注意:在啟動數據庫前,請保證你的openfire服務器沒有啟動。要不然你是無法啟動的。因為你啟動了openfire服務器的話,數據庫已經在使用了,Openfire會鎖定數據庫的。因為openfire不希望在讀寫數據的時候,有人在干預它,導致存在臟讀,重寫的情況。

 

3.2 啟動后就可以看到如下界面

clip_image006

在空白區域你可以寫你的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.entity;
 
import java.sql.Timestamp;
import org.jivesoftware.util.JiveConstants;
 
/**
 * <b>function:</b> 聊天記錄對象實體
 * @author hoojo
 * @createDate 2012-9-19 下午08:28:03
 * @file ChatLogs.java
 * @package com.hoo.openfire.chat.logs.entity
 * @project OpenfirePlugin
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class ChatLogs {
    
    private long messageId;
    private String sessionJID;
    private String sender;
    private String receiver;
    private Timestamp createDate;
    private String content;
    private String detail;
    private int length;
    private int state; // 1 表示刪除
    
    public interface LogState {
        int show = 0;
        int remove = 1;
    }
    
    /**
     * <b>function:</b> 自增id序列管理器,類型變量
     * @author hoojo
     * @createDate 2012-9-20 下午02:38:52
     * @file ChatLogs.java
     * @package com.hoo.openfire.chat.logs.entity
     * @project OpenfirePlugin
     * @blog http://blog.csdn.net/IBM_hoojo
     * @email hoojo_@126.com
     * @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) {
        super();
        this.sessionJID = sessionJID;
        this.createDate = createDate;
        this.content = content;
        this.detail = detail;
        this.length = length;
    }
    
    public ChatLogs(long messageId, String sessionJID, Timestamp createDate, String content, String detail, int length, int state) {
        super();
        this.messageId = messageId;
        this.sessionJID = sessionJID;
        this.createDate = createDate;
        this.content = content;
        this.detail = detail;
        this.length = length;
        this.state = state;
    }
    
    // setter/getter
}

二、開發聊天記錄插件

按照上面給出的工程的目錄結構,新建我們需要的文件。

1、 在src/plugins/chatlogs目錄新建包com.hoo.openfire.chat.logs,在包中建立DbChatLogsManager 聊天記錄CRUD數據庫操作類

package com.hoo.openfire.chat.logs;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.jivesoftware.database.DbConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import com.hoo.openfire.chat.logs.entity.ChatLogs;
 
/**
 * <b>function:</b> 聊天記錄db操作類
 * @author hoojo
 * @createDate 2012-9-19 下午04:15:43
 * @file DbChatLogsManager.java
 * @package com.iflashbuy.openfire.chat.logs
 * @project OpenfirePlugin
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @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 2012-9-19 下午08:13:33
     * @return 最后一個記錄id
     */
    public int getLastId() {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        int count = -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 (SQLException sqle) {
            Log.error(sqle.getMessage(), sqle);
            return 0;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
        return count;
    }
    
    /**
     * <b>function:</b> 獲取總數量
     * @author hoojo
     * @createDate 2012-9-19 下午08:14:59
     * @return 總數量
     */
    public int getCount() {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        int count = -1;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOGS_COUNT);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                count = rs.getInt(1);
            } else {
                count = 0;
            }
        } catch (SQLException sqle) {
            Log.error(sqle.getMessage(), sqle);
            return 0;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
        return count;
    }
    
    /**
     * <b>function:</b> 刪除聊天記錄信息
     * @author hoojo
     * @createDate 2012-9-19 下午08:25:48
     * @param id 聊天信息id
     * @return
     */
    public boolean remove(Integer id) {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOGS_REMOVE);
            pstmt.setInt(1, id);
            return pstmt.execute();
            
        } catch (SQLException sqle) {
            Log.error("chatLogs remove exception: {}", sqle);
            return false;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
    
    /**
     * <b>function:</b> 添加聊天記錄信息
     * @author hoojo
     * @createDate 2012-9-19 下午08:37:52
     * @param logs ChatLogs 聊天記錄對象
     * @return 是否添加成功
     */
    public boolean add(ChatLogs logs) {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            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);
            return false;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
    
    /**
     * <b>function:</b> 通過id查詢聊天記錄信息
     * @author hoojo
     * @createDate 2012-9-19 下午09:32:19
     * @param id 消息id
     * @return ChatLogs
     */
    public ChatLogs find(int id) {
        Connection con = null;
        PreparedStatement pstmt = null;
        ChatLogs logs = null;
        
        try {
            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"));
            }
            return logs;
        } catch (SQLException sqle) {
            Log.error("chatLogs find exception: {}", sqle);
            return logs;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
    
    /**
     * <b>function:</b> 多條件搜索查詢,返回List&lt;ChatLogs>集合
     * @author hoojo
     * @createDate 2012-9-19 下午09:34:45
     * @param entity ChatLogs
     * @return 返回List&lt;ChatLogs>集合
     */
    public List<ChatLogs> query(ChatLogs entity) {
        Connection con = null;
        Statement pstmt = null;
        ChatLogs logs = null;
 
        List<ChatLogs> result = new ArrayList<ChatLogs>();
        
        try {
            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() + "')";
                    
                } else {
                    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);
            }
            return result;
        } catch (SQLException sqle) {
            Log.error("chatLogs search exception: {}", sqle);
            return result;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
    
    /**
     * <b>function:</b> 多條件搜索查詢,返回List<Map>集合
     * @author hoojo
     * @createDate 2012-9-19 下午09:33:28
     * @param entity ChatLogs
     * @return List<HashMap<String, Object>>
     */
    public List<HashMap<String, Object>> search(ChatLogs entity) {
        Connection con = null;
        Statement pstmt = null;
 
        List<HashMap<String, Object>> result = new ArrayList<HashMap<String, Object>>();
        
        try {
            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() + "')";
                    
                } else {
                    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 <= columnCount; ++i) {  
                    String columnVal = rs.getString(i);  
                    if (columnVal == null) {  
                        columnVal = "";  
                    }  
                    map.put(rsmd.getColumnName(i), columnVal);  
                }  
                /** 把裝有一行數據的HashMap存入list */  
                result.add(map);  
            }  
            return result;
        } catch (SQLException sqle) {
            Log.error("chatLogs search exception: {}", sqle);
            return result;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
    
    /**
     * <b>function:</b> 最近聯系人
     * @author hoojo
     * @createDate 2013-3-24 下午4:38:51
     * @param entity 聊天記錄實體
     * @return 最近聯系人集合
     */
    public List<String> findLastContact(ChatLogs entity) {
        Connection con = null;
        PreparedStatement pstmt = null;
        List<String> result = new ArrayList<String>();
        
        try {
            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"));
            }
            return result;
        } catch (SQLException sqle) {
            Log.error("chatLogs find exception: {}", sqle);
            return result;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
    
    /**
     * <b>function:</b> 查找所有聊天用戶
     * @author hoojo
     * @createDate 2013-3-24 下午4:37:40
     * @return 所有聊天用戶sessionJID集合
     */
    public List<String> findAllContact() {
        Connection con = null;
        PreparedStatement pstmt = null;
        List<String> result = new ArrayList<String>();
        
        try {
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOGS_ALL_CONTACT);
            ResultSet rs = pstmt.executeQuery();
            while (rs.next()) {
                result.add(rs.getString("sessionJID"));
            }
            return result;
        } catch (SQLException sqle) {
            Log.error("chatLogs find exception: {}", sqle);
            return result;
        } finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
    }
}

比較簡單,都是數據庫的增刪改查的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.

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.logs;
 
import java.io.File;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import org.jivesoftware.database.SequenceManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Presence;
import com.hoo.openfire.chat.logs.entity.ChatLogs;
import com.hoo.openfire.chat.logs.entity.ChatLogs.ChatLogsConstants;
 
/**
 * <b>function:</b> 聊天記錄插件
 * @author hoojo
 * @createDate 2012-9-19 下午01:47:20
 * @file ChatLogsPacketInterceptor.java
 * @package com.hoo.openfire.chat.logs
 * @project OpenfirePlugin
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class ChatLogsPlugin implements PacketInterceptor, Plugin {
 
    private static final Logger log = LoggerFactory.getLogger(ChatLogsPlugin.class);
    
    private static PluginManager pluginManager;
    private static DbChatLogsManager logsManager;
    
    public ChatLogsPlugin() {
        interceptorManager = InterceptorManager.getInstance();
        logsManager = DbChatLogsManager.getInstance();
    }
 
    //Hook for intercpetorn
    private InterceptorManager interceptorManager;
    
    /**
     * <b>function:</b> 攔截消息核心方法,Packet就是攔截消息對象
     * @author hoojo
     * @createDate 2013-3-27 下午04:49:11
     */
    @Override
    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)) {
                return;
            } else if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) {
                // 非當前openfire服務器信息
                return;
            } else if ("".equals(recipient.getResource())) {
            }
        }
        this.doAction(packet, incoming, processed, session);
    }
 
    /**
     * <b>function:</b> 執行保存/分析聊天記錄動作
     * @author hoojo
     * @createDate 2013-3-24 下午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) 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));
                
            // 群聊天,多人模式
            } else if (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());
            }
        } else if (packet instanceof IQ) {
            IQ iq = (IQ) copyPacket;
            if (iq.getType() == IQ.Type.set && iq.getChildElement() != null && "session".equals(iq.getChildElement().getName())) {
                log.info("用戶登錄成功:{}", iq.toXML());
                debug("用戶登錄成功:" + iq.toXML());
            }
        } else if (packet instanceof Presence) {
            Presence presence = (Presence) copyPacket;
            if (presence.getType() == Presence.Type.unavailable) {
                log.info("用戶退出服務器成功:{}", presence.toXML());
                debug("用戶退出服務器成功:" + presence.toXML());
            }
        } 
    }
    
    /**
     * <b>function:</b> 創建一個聊天記錄實體對象,並設置相關數據
     * @author hoojo
     * @createDate 2013-3-27 下午04:44:54
     * @param packet 數據包
     * @param incoming 如果為ture就表明是發送者
     * @param session 當前用戶session
     * @return 聊天實體
     */
    private ChatLogs get(Packet packet, boolean incoming, Session session) {
        Message message = (Message) packet;
        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);
        
        return logs;
    }
    
    /**
     * <b>function:</b> 調試信息
     * @author hoojo
     * @createDate 2013-3-27 下午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);
        
        log.info("id:" + session.getStreamID() + ", address: " + session.getAddress());
        log.info("info: {}", info);
        log.info("plugin Name: " + pluginManager.getName(this) + ", xml: " + packet.toXML());
    }
    
    private void debug(Object message) {
        if (true) {
            System.out.println(message);
        }
    }
    
    @Override
    public void destroyPlugin() {
        interceptorManager.removeInterceptor(this);
        debug("銷毀聊天記錄插件成功!");
    }
 
    @Override
    public void initializePlugin(PluginManager manager, File pluginDirectory) {
         interceptorManager.addInterceptor(this);
         pluginManager = manager;
         
         debug("安裝聊天記錄插件成功!");
    }
}

注意在初始化插件的時候,在系統的爛機器管理器中添加對當前插件對象的管理,即在interceptorManager中addInterceptor。而在銷毀資源的時候則removeInterceptor當前對象。

上面已經在打印出用戶聊天的Packet信息,當用戶登陸、退出、發送消息等,都會被攔截到。我們只需要攔截我們要的消息數據,注意下面看這段代碼

// 程序執行中;是否為結束或返回狀態(是否是當前session用戶發送消息)
if (processed || !incoming) {
    return;
}

如果沒有這段代碼,那我們就可以保存很多重復或無用的信息。為什么這樣寫,看看在控制台或日子infohttp://127.0.0.1:9090/logviewer.jsp?log=info中的信息:

一個用戶hoojo和離線用戶boy聊天Packet內容

################### start ###################1364442703632
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 #####################1364442703632
 
################### start ###################1364442703659
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 #####################1364442703659

主要看incoming和processed的值:incoming都為ture,incoming為true就表示是自己發送的信息。而procesed為false,然后才是true,processed為true就表示發送結束。且session都是當前聊天用戶。

hoojo@127.0.0.1/Spark 2.6.3 incoming:true,processed:false;

--> hoojo@127.0.0.1/Spark 2.6.3 incoming:true,processed:true;

通過這個狀態,我們的判斷代碼應該可以攔截到的是 第一種狀態。然后就可以將一種狀態的Packet保存到聊天記錄表中,其他的數據暫不處理!

用戶hoojo和一個在線用戶boy聊天Packet

################### start ###################1364443976157
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 #####################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/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 #####################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/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 #####################1364443976228
################### start ###################1364443976232
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 #####################1364443976232

 

狀態流程:

hoojo@127.0.0.1/Spark 2.6.3 incoming:true,processed:false;

--> boy@127.0.0.1/WebIM incoming:false,processed:false;

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

-->hoojo@127.0.0.1/Spark 2.6.3 incoming:true,processed:true;

而我們保存消息的狀態是在第一個狀態,即incoming=true,processed=false的這個狀態保存的。

 

看圖,這樣更利於理解

openfire-send-message-status

離線用戶的流程就是沒有紅色部分的,其他用戶就是整體的流程部分。

 

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

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
    <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 [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>        
        <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>
        </tab>
    </adminconsole>
</plugin>

如果你看過上一篇文章,這個配置你就不陌生了。最主要的還是class這個配置。在上面的配置中有一個adminconsole配置是頁面的配置,暫且忽略。稍后再提,ok!

至此插件的核心部分已經配置完成了,如果你現在打包部署的話,肯定是可以保存到數據庫。如果你想試試的話,可以按照上一篇文章的步驟進行打包部署。打包不帶jsp的ant命令-java-plug-jar即可。這里我就不運行這步,繼續完成其他的操作演示。

 

4、 在src/plugins/chatlogs目錄中的com.hoo.openfire.chat.logs包下新建ChatLogsServlet,提供對外調用的接口,一般用它查詢聊天記錄和聯系人信息

package com.hoo.openfire.chat.logs;
 
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.codehaus.jackson.map.ObjectMapper;
import org.jivesoftware.admin.AuthCheckFilter;
import org.jivesoftware.util.ParamUtils;
 
import com.hoo.openfire.chat.logs.entity.ChatLogs;
 
/**
 * <b>function:</b> 聊天記錄插件對外接口
 * 
 * @author hoojo
 * @createDate 2012-9-18 下午09:32:21
 * @file ChatLogsServlet.java
 * @package com.hoo.openfire.chat.logs
 * @project OpenfirePlugin
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
public class ChatLogsServlet extends HttpServlet {
 
    private static final long serialVersionUID = 6981863134047161005L;
    private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    private static final ObjectMapper mapper = new ObjectMapper();
    private static DbChatLogsManager logsManager;
 
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        
        logsManager = DbChatLogsManager.getInstance();
        
        // 取消權限驗證,不登陸即可訪問
        AuthCheckFilter.addExclude("chatlogs");
        AuthCheckFilter.addExclude("chatlogs/ChatLogsServlet");
    }
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
 
    @Override
    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");
 
            try {
                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);
        } else {
            String sender = ParamUtils.getParameter(request, "sender");
            String receiver = ParamUtils.getParameter(request, "receiver");
            String content = ParamUtils.getParameter(request, "content");
            String createDate = ParamUtils.getParameter(request, "createDate");
 
            try {
                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);
        }
    }
 
    @Override
    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""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>

這里我配置的是/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管理員控制台的配置。其中<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">
<html>
  <head>
    <title>ChatLogs 聊天記錄 openfire plugin</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="pageID" content="chatLogs-service"/>
  </head>
  
  <%
      //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");
    try {
        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);
  %>
  
  <body>
    <div class="jive-contentBoxHeader">所有聊天用戶</div>
    <div class="jive-contentBox">
        <a href="${pageContext.request.contextPath }/plugins/chatlogs?action=all!contact">查看</a>
    </div>
    
    <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">
        </form>
    </div>
    
    <div class="jive-table">
        <table cellpadding="0" cellspacing="0" border="0" width="100%">
        <thead>
            <tr>
                <th>發送人</th>
                <th>接收者</th>
                <th>內容</th>
                <th>發送時間</th>
                <th>刪除</th>
            </tr>
        </thead>
        <tbody>
            <% for (int i = 0, len = logs.size(); i < len; 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">
                </a></td>
            </tr>
            <% } %>
         </tbody>
        </table>
    </div>
  </body>
</html>

 

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">
<html>
  <head>
    <title>ChatLogs 聊天記錄 openfire plugin</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="pageID" content="chatLogs-all-contact-service"/>
  </head>
  
  <body>
    <div class="jive-contentBoxHeader">ChatLogs 所有聊天聯系人</div>
    
    <div class="jive-table">
        <table cellpadding="0" cellspacing="0" border="0" width="100%">
        <thead>
            <tr>
                <th>聯系人JID</th>
                <th>他/她的聊天記錄</th>
                <th>【他/她】的聯系人</th>
            </tr>
        </thead>
        <tbody>
            <% 
            Object obj = request.getAttribute("allContact");
            if (obj != null) {
                List<String> allContact = (List<String>) obj;
                for (int i = 0, len = allContact.size(); i < len; i++) {
                    String contact = allContact.get(i);
                    JID jid = new JID(contact);
                %>
                <tr class="jive-<%= i % 2 == 0 ? "even" : "odd" %>">
                       <td><%=contact %></td>
                    <td>
                        <a href="${pageContext.request.contextPath }/plugins/chatlogs/chatLogs-service.jsp?sender=<%=jid.getNode() %>">他/她的聊天記錄</a>
                    </td>
                    <td>
                        <a href="${pageContext.request.contextPath }/plugins/chatlogs?action=last!contact&sender=<%=jid.getNode() %>">他/她的聯系人</a>
                    </td>
                </tr>
            <% }
            }
            %>
         </tbody>
        </table>
    </div>
  </body>
</html>

 

chatLogs-last-contact-service.jsp查詢某個用戶的最近聯系人

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>ChatLogs 聊天記錄 openfire plugin</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="pageID" content="chatLogs-all-contact-service"/>
  </head>
  
  <body>
    <div class="jive-contentBoxHeader">ChatLogs 所有聊天聯系人</div>
    
    <div class="jive-table">
        <table cellpadding="0" cellspacing="0" border="0" width="100%">
        <thead>
            <tr>
                <th>聯系人JID</th>
                <th>【他/她】的聯系人</th>
                <th>他/她的聊天記錄</th>
            </tr>
        </thead>
        <tbody>
            <% 
            Object obj = request.getAttribute("lastContact");
            if (obj != null) {
                List<String> allContact = (List<String>) obj;
                for (int i = 0, len = allContact.size(); i < len; i++) {
                    String contact = allContact.get(i);
                %>
                <tr class="jive-<%= i % 2 == 0 ? "even" : "odd" %>">
                       <td><%=contact %></td>
                    <td>
                        <a href="${pageContext.request.contextPath }/plugins/chatlogs/chatLogs-service.jsp?sender=${param.sender}&receiver=<%=contact%>">和他/她的聊天記錄信息</a>
                    </td>
                    <td>
                        <a href="${pageContext.request.contextPath }/plugins/chatlogs/chatLogs-service.jsp?receiver=<%=contact%>">和他/她的聊天記錄信息</a>
                    </td>
                </tr>
            <% }
            }
            %>
         </tbody>
        </table>
    </div>
  </body>
</html>

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}" />
        
        <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包 -->
    <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/**" />
        </javac>
        
        <jar jarfile="${jsp.jar}" basedir="${webapp.path}/jsp-dist/classes" />
    </target>
 
    <!-- 將java servlet打包成jar -->
    <target name="java-jar">
        <mkdir dir="${java.jar.dir}"/>
        <jar jarfile="${java.jar}">
            <fileset dir="${webapp.path}/bin" includes="**/*.class"/>
        </jar>    
    </target>    
 
    <!-- 生成可部署的插件包 -->
    <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>
        <copy todir="${webapp.path}/${plugin.name}/web">
            <fileset dir="${plugin.path}/web">
                <include name="*"/>
                <include name="**/*.*"/>
                <exclude name="**/*.xml"/>
                <exclude name="**/*.jsp"/>
            </fileset>
        </copy>
        <!-- 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>
        <!-- 將國際化相關資源文件復制到插件目錄下 
        <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="**/**"/>
        </jar>    
    </target>    
    
    <!-- 生成沒有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="*.*"/>
        </copy>
        
        <!-- 產生可部署插件包 -->
        <jar jarfile="${webapp.path}/${plugin.name}.jar">
            <fileset dir="${webapp.path}/${plugin.name}" includes="**/**"/>
        </jar>    
    </target>
                    
    <!-- 清理生成的文件 -->
    <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>
    
    <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包了。

 

小提示:如果你的插件發布成功,但是沒有達到預期的效果。那么很有可能插件中的代碼出現了異常信息。你可以進入管理員控制台的日志http://127.0.0.1:9090/logviewer.jsp看到操作系統的日志信息。日志內容有常用的幾個級別,經常看看日志對開發插件有幫助的。我攔截消息包的時候是分析消息包的xml內容作的判斷,才完成這個插件的開發的。

 

發布插件:直接將插件放置在openfire服務器的plugins目錄下。我的是在:C:\Program Files\openfire\plugins目錄。重起openfire后你可以看到控制台輸出我們插件中輸出的內容,並且在C:\Program Files\openfire\plugins目錄中可以看到該目錄下多了一個chatlogs的目錄(openfire可以自動解壓jar包)。

clip_image010

當你在關閉服務器的瞬間,也會打印銷毀插件的消息。

 

插件按照成功后,訪問http://localhost:9090/plugin-admin.jsp頁面你就可以看到安裝好的插件了。

clip_image012

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

 

3、 測試插件

我們開一個spark和一個WebIM聊天程序進行互相聊天。內容大概如下:

clip_image014

clip_image016

發送聊天內容的時候你可以看到啟動的openfire控制台有一些我們打印的報文。當聊天終止后,我們可以關閉openfire服務器。然后啟動HSQL數據庫查看下ofChatLogs這張表的數據,如果數據保存成功。那么效果達到了,如果沒有消息內容,你得看看是哪里錯誤或遺漏了。建議你看看http://127.0.0.1:9090/logviewer.jsp的日志,可能會有提示信息。我這里查詢后,大致內容如下:

image

OK,下面我們可以看看在 openfire管理員控制台服務器設置左側菜單中的ChatLogs Servcie http://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/Spark 2.6.3]

 

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

[hoojo@127.0.0.1/Spark 2.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 構建 Web IM 遠程及時聊天通信程序】 優美清新的界面,可以多窗口聊天

Smack 結合 Openfire服務器,建立IM通信,發送聊天消息】 可以基於他開發Java的聊天應用

Apache MiNa 實現多人聊天室】 多人聊天室,如果結合Smack和Openfire,就可以實現外網聊天應用

JavaScript/jQuery WebIM 及時聊天通信工具 本地客戶端】 本地應用,不需要Openfire服務器

Openfire與XMPP協議】理論知識,便於連接Openfire

Jwchat 的安裝和配置、Service unavailable、Authorization failed問題匯總】 拓展你的應用,可以了解開源的jwchat,全JS的應用

移動應用(手機應用)開發IM聊天程序解決方案】 移動手機和Openfire的整合方案

Spring整合DWR comet 實現無刷新 多人聊天室】 DWR實現聊天應用,簡單快速


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM