http://www.ox-holdings.com

二、开发聊天记录插件, 项目源码 

摘要XMPP开源IM客户端Spark近日发布了2.8.0版,该版本是个主要更新版本,标志着对 Java 7 的支持已经结束,从 2.8.0 起,在线的安装包在 Java 7 下将不能运行,官方建议升级至 Java 8。前言XMPP开源IM客户端Spark近日发布了2.8.0版,该版本是个主要更新版本,标志着对 Java 7 的支持已经结束,从 2.8.0 起,在线的安装包在 Java 7 下将不能运行,官方建议升级至 Java 8。Spark是一个开源、跨平台IM客户端(支持Linux、windows、Mac OS X平台)。它的特性支持集组聊天,电话集成和强大安全性能。如果企业内部部署IM使用Openfire+Spark是最佳的组合。官方的说明原文This is a major release marking the end of support for Java 7 (which has reached the end of life state more than a year ago). Full Spark installers have already been bundled with Java 8 for some time. Online installer won't work with Java 7 starting with 2.8.0 version. We recommend updating to Java 8. Underlying Smack library has been updated to the latest version in this release. Roar plugin has received new popup control options as well.(官方原文: 版更新内容Bug[SPARK-1130] - Spark behaves wrong with chats with similar names[SPARK-1275] - Add contact dialog makes Spark lose connection[SPARK-1564] - Should set priority to 0 when Idle status turns on[SPARK-1613] - Long lines of text without spaces break the word wrap[SPARK-1736] - NPE in Roar plugin prevents messages to show up on fresh profiles[SPARK-1738] - RPM build fails after Smack 4 update[SPARK-1739] - Not trusting Openfire certificate after Smack 4 update[SPARK-1742] - Spark won't start when bookmark service is unavailable.[SPARK-1743] - MUC nickname sharing fails.[SPARK-1744] - Shouldnt send chat state notifications when the chat isn't active yet[SPARK-1745] - Fastpath plugin doesn't start after Smack 4 update[SPARK-1747] - Spark appears to fall back to a non-sasl when authenticating[SPARK-1749] - Not showing the context menu for participants in MUC[SPARK-1750] - Spark is not automatically reconnecting after Smack 4 update[SPARK-1751] - Only a few first messages show up and no other messages come through[SPARK-1752] - File transfer and screenshot sending doesn't work after Smack 4 update[SPARK-1755] - Redundant Roar popups and history entries when receiving messages[SPARK-1756] - Prevent NPE in error handling (Group chat - Smack 4)[SPARK-1761] - Fixing RPM build after Bamboo migration[SPARK-1763] - Can't create new account on a server (Smack 4 issue)[SPARK-1764] - Transcript file is not being closed on exit[SPARK-1766] - File Streams not being closed on a few files[SPARK-1768] - Fix Debian build[SPARK-1772] - Not showing correct room affiliation icons for different roles[SPARK-1773] - Spark does not detect keystrokes when switched away due to idle on mac and linux[SPARK-1776] - Private messages in MUC do not show up (Smack 4)[SPARK-1777] - Presence getting stuck in Away mode sometimes[SPARK-1778] - Spark shouldn't lookup an empty username[SPARK-1779] - No vertical scrollbar in the participants list[SPARK-1783] - ROAR plugin should not assume Opaque is supportedNew Feature[SPARK-1596] - Different settings for group chat Roar popups[SPARK-1599] - Add an option for custom Roar popup based on a keyword[SPARK-1709] - Add Russian dictionary for spellchecker[SPARK-1729] - Add an option to save group chat password[SPARK-1731] - Add Ukrainian translationTask[SPARK-1722] - Update bundled JRE with the latest versionImprovement[SPARK-1559] - Add URL support to server broadcast dialog[SPARK-1567] - Update Smack to the 4th version (4.1.7)[SPARK-1646] - Should switch to Away mode when locking screen on Windows 10[SPARK-1735] - Small group chat user-interface improvements[SPARK-1741] - Replace old code constructs[SPARK-1746] - Update Lithuanian translation[SPARK-1753] - Don't be disconnected when receiving invalid stanzas[SPARK-1754] - Update Italian translation[SPARK-1769] - Update Substance LaF to the latest version[SPARK-1771] - Update Smack to 4.1.8 version[SPARK-1775] - Prevent exceptions from going unnoticed更多版本更新记录,请参见:

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

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

如果你没有看上一篇文章的话,请你还是看看。

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

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

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:

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

 

推荐文章:

【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服务器,就可以用了。如果出现什么问题的话,你可以看看这篇文章,都有解决方法。

插件下载:

 

基本原理(流程)

图片 1

 

【转】Create Hello-JNI with Android Studio

Overview

This tutorial introduces SignalR development by showing how to build a simple browser-based chat application.  You will add the SignalR library to an empty ASP.NET web application, create a hub class for sending messages to clients, and create an HTML page that lets users send and receive chat messages. For a similar tutorial that shows how to create a chat application in MVC 4 using an MVC view, see Getting Started with SignalR and MVC 4.

Note: This tutorial uses the release (1.x) version of SignalR.  For details on changes between SignalR 1.x and 2.0, see Upgrading SignalR 1.x Projects.

SignalR is an open-source .NET library for building web applications that require live user interaction or real-time data updates.  Examples include social applications, multiuser games, business collaboration, and news, weather, or  financial update applications. These are often called real-time applications.

SignalR simplifies the process of building real-time applications.  It includes an ASP.NET server library and a JavaScript client library to make it easier to manage client-server connections and push content updates to clients. You can add the SignalR library to an existing ASP.NET application to gain real-time functionality. 

The tutorial demonstrates the following SignalR development tasks:

 

  • Adding the SignalR library to an ASP.NET web application.
  • Creating a hub class to push content to clients.
  • Using the SignalR jQuery library in a web page to send messages and display updates from the hub.

The following screen shot shows the chat application running in a browser. Each new user can post comments and see comments added after the user joins the chat. 

图片 2

Sections:

  • Set up the Project
  • Run the Sample
  • Examine the Code
  • Next Steps

一、准备工作

1、 这里的开发环境就是上一篇文章的开发环境,如果你还没有配置好环境或不知道怎么样配置。那么烦请你按照上一篇博文的讲述方法配置好开发环境,然后跟着我一步步开发聊天记录插件。

2、 基于之前讲的,现在在环境中创建好插件的目录结构。新建一个plugins/chatlogs目录,新插件的目录文件如下,里面的部分文件是通用的,只有在src/plugins/chatlogs目录中文件才是这次新增的文件。这次的插件目录结构会按照下面的结构来。

图片 3

先熟悉下上面的目录,就简单大致介绍下上面的目录。在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 Filesopenfirebinextra,在该目录下你可以看到

图片 4

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

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

 

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

图片 5

在空白区域你可以写你的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

}

 

From:

Set up the Project

This section shows how to create an empty ASP.NET web application, add SignalR, and create the chat application.

Prerequisites: 

  • Visual Studio 2010 SP1 or 2012.  If you do not have Visual Studio, see ASP.NET Downloads to get the free Visual Studio 2012 Express Development Tool.
  • Microsoft ASP.NET and Web Tools 2012.2. For Visual Studio 2012, this installer adds new ASP.NET features including SignalR templates to Visual Studio.  For Visual Studio 2010 SP1, an installer is not available but you can complete the tutorial by installing the SignalR NuGet package as described in the setup steps.

The following steps use Visual Studio 2012 to create an ASP.NET Empty Web Application and add the SignalR library:

  1. In Visual Studio create an ASP.NET Empty Web Application.

    图片 6

  2. In Solution Explorer, right-click the project, select Add | New Item, and select the SignalR Hub Class item.  Name the class ChatHub.cs and add it to the project.  This step creates the ChatHub class and adds to the project a set of script files and assembly references that support SignalR.

    图片 7

    Note: You can also add SignalR to a project by opening the Tools | Library Package Manager | Package Manager Console and running a command: install-packageMicrosoft.AspNet.SignalR.  If you use the console to add SignalR, create the SignalR hub class as a separate step after you add SignalR.

  3. In Solution Explorer expand the Scripts node. Script libraries for jQuery and SignalR are visible in the project.  

    图片 8

  4. Replace the code in the new ChatHub class with the following code.

    usingSystem;usingSystem.Web;usingMicrosoft.AspNet.SignalR;namespaceSignalRChat{publicclassChatHub:Hub{publicvoidSend(string name,string message){// Call the broadcastMessage method to update clients.Clients.All.broadcastMessage(name, message);}}}
    
  5. In Solution Explorer, right-click the project, then click Add | New Item. In the Add New Item dialog, select Global Application Class and click Add.

    图片 9

  6. Add the following using statements after the provided using statements in the Global.asax.cs class.

    usingSystem.Web.Routing;usingMicrosoft.AspNet.SignalR;
    
  7. Add the following line of code in the Application_Start method of the Global class to register the default route for SignalR hubs.

    // Register the default hubs route: ~/signalr/hubsRouteTable.Routes.MapHubs();
    
  8. In Solution Explorer, right-click the project, then click Add | New Item. In the Add New Item dialog, select Html Page and click Add.

  9. In Solution Explorer, right-click the HTML page you just created and click Set as Start Page

  10. Replace the default code in the HTML page with the following code.

    <!DOCTYPE html><html><head><title>SignalR Simple Chat</title><styletype="text/css">.container {background-color:#99CCFF;border: thick solid #808080;padding:20px;margin:20px;}</style></head><body><divclass="container"><inputtype="text"id="message"/><inputtype="button"id="sendmessage"value="Send"/><inputtype="hidden"id="displayname"/><ulid="discussion"></ul></div><!--Script references. --><!--Reference the jQuery library. --><scriptsrc="/Scripts/jquery-1.8.2.min.js"></script><!--Reference the SignalR library. --><scriptsrc="/Scripts/jquery.signalR-1.0.0.js"></script><!--Reference the autogenerated SignalR hub script. --><scriptsrc="/signalr/hubs"></script><!--Add script to update the page and send messages.--><scripttype="text/javascript">
            $(function(){// Declare a proxy to reference the hub. var chat = $.connection.chatHub;// Create a function that the hub can call to broadcast messages.
                chat.client.broadcastMessage =function(name, message){// Html encode display name and message. var encodedName = $('<div />').text(name).html();var encodedMsg = $('<div />').text(message).html();// Add the message to the page. 
                    $('#discussion').append('<li><strong>'+ encodedName
                        +'</strong>:&nbsp;&nbsp;'+ encodedMsg +'</li>');};// Get the user name and store it to prepend to messages.
                $('#displayname').val(prompt('Enter your name:',''));// Set initial focus to message input box.  
                $('#message').focus();// Start the connection.
                $.connection.hub.start().done(function(){
                    $('#sendmessage').click(function(){// Call the Send method on the hub. 
                        chat.server.send($('#displayname').val(), $('#message').val());// Clear text box and reset focus for next comment. 
                        $('#message').val('').focus();});});});</script></body></html>
    
  11. Save All for the project.

二、开发聊天记录插件

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

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;

}

 

如果没有这段代码,那我们就可以保存很多重复或无用的信息。为什么这样写,看看在控制台或日子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的这个状态保存的。

 

看图,这样更利于理解

图片 10

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

 

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,但是我在后面调用的时候是无法调用到的。;但如果我用就可以调用到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,至此整个插件基本编写完成,下面就开始打包部署,打包是很关键的。

 

访问需要FQ。

Run the Sample

  1. Press F5 to run the project in debug mode. The HTML page loads in a browser instance and prompts for a user name.

    图片 11

  2. Enter a user name.

  3. Copy the URL from the address line of the browser and use it to open two more browser instances. In each browser instance, enter a unique user name.

  4. In each browser instance, add a comment and click Send. The comments should display in all browser instances.

    Note: This simple chat application does not maintain the discussion context on the server. The hub broadcasts  comments to all current users. Users who join the chat later will see messages added from the time they join.

    The following screen shot shows the chat application running in three browser instances, all of which are updated  when one instance sends a message:

    图片 12

  5. In Solution Explorer, inspect the Script Documents node for the running application. There is a script file named hubs that the SignalR library dynamically generates at runtime. This file manages the communication between jQuery script and server-side code. 

    图片 13

三、打包发布插件

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 Filesopenfirelib这个目录中,这样整个环境中就可以使用这个jar包了。

 

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

 

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

图片 14

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

 

插件按照成功后,访问页面你就可以看到安装好的插件了。

图片 15

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

 

3、 测试插件

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

图片 16

图片 17

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

图片 18

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

 

随后,我们应该测试下我们编写的ChatLogsServlet对外的查询接口。在浏览器中直接请求:会返回一段JSON数据,存放所有的聊天用户的sessionJID。

["boy@127.0.0.1/WebIM","girl@127.0.0.1/WebIM",hoojo@127.0.0.1/Spark 2.6.3]

 

请求:

[hoojo@127.0.0.1/Spark 2.6.3]

 

请求:

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

 

请求:

查询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实现聊天应用,简单快速

 

 

转自:http://www.cnblogs.com/hoojo/archive/2013/03/29/openfire_plugin_chatlogs_plugin_.html 

没有翻译成中文是因为图片很详细,看不懂英文,根据图片一步一步也能完成。另外开发人员应该具备阅读英文技术博客的能力。

Examine the Code

The SignalR chat application demonstrates two basic SignalR development tasks:  creating a hub as the main coordination object on the server, and using the SignalR jQuery library to send and receive messages.

1. Overview

SignalR Hubs

In the code sample the ChatHub class derives from the Microsoft.AspNet.SignalR.Hub class. Deriving from the Hub class is a useful way to build a SignalR application.  You can create public methods on your hub class and then access those methods by calling them from jQuery scripts in a web page.

In the chat code, clients call the ChatHub.Send method to send a new message.  The hub in turn sends the message to all clients by calling Clients.All.broadcastMessage.

The Send method demonstrates several hub concepts :

  • Declare public methods on a hub so that clients can call them.
  • Use the Microsoft.AspNet.SignalR.Hub.Clients dynamic property to access all clients connected to this hub.
  • Call a jQuery function on the client (such as the broadcastMessage function) to update clients.

    publicclassChatHub:Hub{publicvoidSend(string name,string message){// Call the broadcastMessage method to update clients.Clients.All.broadcastMessage(name, message);}}
    

图片 19

SignalR and jQuery

The HTML page in the code sample shows how to use the SignalR jQuery library to communicate with a SignalR hub. The essential tasks in the code are declaring a proxy to reference the hub, declaring a function that the server can call to push content to clients, and starting a connection to send messages to the hub. 

The following code declares a proxy for a hub. 

var chat = $.connection.chatHub;

 

Note: In jQuery the reference to the server class and its members is in camel case.  The code sample references the C# ChatHub class in jQuery as chatHub.

The following code is how you create a callback function in the script.  The hub class on the server calls this function to push content updates to each client. The two lines that HTML encode the content before displaying it are optional and show a simple way to prevent script injection.

    chat.client.broadcastMessage =function(name, message){// Html encode display name and message. var encodedName = $('<div />').text(name).html();var encodedMsg = $('<div />').text(message).html();// Add the message to the page. 
        $('#discussion').append('<li><strong>'+ encodedName
            +'</strong>:&nbsp;&nbsp;'+ encodedMsg +'</li>');};

 

The following code shows how to open a connection with the hub. The code starts the connection and then passes it a function to handle the click event on the Send button in the HTML page.

Note: This approach insures that the connection is established before the event handler executes. 

    $.connection.hub.start().done(function(){
        $('#sendmessage').click(function(){// Call the Send method on the hub. 
            chat.server.send($('#displayname').val(), $('#message').val());// Clear text box and reset focus for next comment. 
            $('#message').val('').focus();});});

 

In this codelab, you'll learn how to use Android Studio to start Android NDK project development.

Next Steps

You learned that SignalR is a framework for building real-time web applications.  You also learned several SignalR development tasks: how to add SignalR to an ASP.NET application, how to create a hub class, and how to send and receive messages from the hub.

You can make the sample application in this tutorial or other SignalR applications available over the Internet by deploying them to a hosting provider. Microsoft offers free web hosting for up to 10 web sites in a free Windows Azure trial account. For a walkthrough on how to deploy the sample SignalR application, see Publish the SignalR Getting Started Sample as a Windows Azure Web Site. For detailed information about how to deploy a Visual Studio web project to a Windows Azure Web Site, see Deploying an ASP.NET Application to a Windows Azure Web Site. (Note: The WebSocket transport is not currently supported for Windows Azure Web Sites.  When WebSocket transport is not available, SignalR uses the other available transports as described in the Transports section of the Introduction to SignalR topic.)

To learn more advanced SignalR developments concepts, visit the following sites for SignalR source code and resources:

  • SignalR Project
  • SignalR Github and Samples
  • SignalR Wiki

 

2. Create Java Sample App

郑重声明:本文版权归新匍京a奥门-最全网站手机版app官方下载所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。