Openfire插件开发实例

时间:2022-05-08 14:44:29

Openfire插件开发实例

目的:主要是为了监控Openfire各类message,然后对message body做进一步扩展,让Openfire更适合当前的业务需求。

局限:因为时间有限,本插件并没有对Openfire message协议进行扩展,都是基于原生的xml协议,因此后续开发还需要进行更加深入的二次开发操作。

实例下载:http://download.csdn.net/detail/qiu_11/9719268

1.Openfire插件目录结构

src目录主要用于存放插件源码

build目录主要存放打包脚本以及相对应的部分属性配置文件

lib目录主要存放依赖开发的jar,这次开发没有用使用maven模式

2.对插件源码进行分析

ChatLogsDbManager.java该文件主要封装了插件对数据库的操作

/*

 * System Abbrev :

 * system Name  :

 * Component No  :

 * Component Name:

 * File name     :ChatLogsDbManager.java

 * Author        :Peter.Qiu

 * Date          :2016年12月12日

 * Description   :  <description>

 */

 

/* Updation record 1:

 * Updation date        :  2016年12月12日

 * Updator          :  Peter.Qiu

 * Trace No:  <Trace No>

 * Updation No:  <Updation No>

 * Updation Content:  <List all contents of updation and all methods updated.>

 */

package com.qiuzhping.openfire.plugin;

 

import java.sql.Connection;

import java.sql.PreparedStatement;

 

import org.jivesoftware.database.DbConnectionManager;

 

import com.qiuzhping.openfire.plugin.entity.ChatLogs;

 

public class ChatLogsDbManager {

 

         private static final ChatLogsDbManager CHAT_LOGS_MANAGER = new ChatLogsDbManager();

 

         private ChatLogsDbManager() {

 

         }

 

         public static ChatLogsDbManager getInstance() {

                   return CHAT_LOGS_MANAGER;

         }

 

         private static final String LOGS_INSERT = "INSERT INTO ofChatLogs(messageId, sessionJID, sender, receiver, createDate, length, content, detail, state,message_type) VALUES(?,?,?,?,?,?,?,?,?,?)";

 

 

 

         /** <Description functions in a word>

          * 添加聊天记录信息

          * <Detail description>

          * @author Peter.Qiu

          * @param logs

          * @return [Parameters description]

          * @return boolean [Return type description]

          * @exception throws [Exception] [Exception description]

          * @see [Related classes#Related methods#Related properties]

          */

         public boolean add(ChatLogs logs) {

                   Connection con = null;

                   PreparedStatement pstmt = null;

                   try {

                            if (logs == null) {

                                     return false;

                            }

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

                            pstmt.setString(i++, logs.getMessageType() != null ? logs.getMessageType() : "0");

                            return pstmt.execute();

                   } catch (Exception e) {

                            System.out.println("添加聊天记录信息出错" + e);

                            return false;

                   } finally {

                            DbConnectionManager.closeConnection(pstmt, con);

                   }

         }

}

 

ChatLogsInterceptor.java PacketInterceptor,就是消息拦截对象,这个东西非常有用,你可以制作各种实例来处理消息。-- 性能十分影响就看你自己考虑了。比如消息日志,消息过滤等。Interceptor也非常容易使用,在使用的时候使用InterceptorManager 加入,不用的时候删除即可

/*

* System Abbrev :

* system Name  :

* Component No  :

* Component Name:

* File name     :ChatLogsInterceptor.java

* Author        :Peter.Qiu

* Date          :2016年12月14日

* Description   :  <description>

*/

 

/* Updation record 1:

 * Updation date        :  2016年12月14日

 * Updator          :  Peter.Qiu

 * Trace No:  <Trace No>

 * Updation No:  <Updation No>

 * Updation Content:  <List all contents of updation and all methods updated.>

 */

package com.qiuzhping.openfire.plugin;

 

import java.sql.Timestamp;

import java.util.Date;

import java.util.HashMap;

import java.util.Map;

 

import net.sf.json.JSONObject;

 

import org.jivesoftware.database.SequenceManager;

import org.jivesoftware.openfire.interceptor.PacketInterceptor;

import org.jivesoftware.openfire.interceptor.PacketRejectedException;

import org.jivesoftware.openfire.session.Session;

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.qiuzhping.openfire.plugin.entity.ChatLogs;

import com.qiuzhping.openfire.plugin.entity.ChatLogs.ChatLogsConstants;

 

/**

 * <Description functions in a word>

 *

 * <Detail description>

 *

 * @author Peter.Qiu

 * @version  [Version NO, 2016年12月14日]

 * @see  [Related classes/methods]

 * @since  [product/module version]

 */

public class ChatLogsInterceptor implements PacketInterceptor {

 

         public static final String MESSAGE_TYPE = "messageType";// 聊天记录类型

         public static final String BODY_CONTEXT = "bodyContext";// 聊天记录内容

         private ChatLogsDbManager logsManager = ChatLogsDbManager.getInstance();

        

         public ChatLogsInterceptor() {

 

         }

         /**

          * 拦截消息核心方法,Packet就是拦截消息对象

          * @param packet

          * @param session

          * @param incoming

          * @param processed

          * @throws PacketRejectedException

          */

         @Override

         public void interceptPacket(Packet packet, Session session,

                            boolean incoming, boolean processed) throws PacketRejectedException {

                   if (session != null) {

                            debug(packet, incoming, processed, session);

                   }

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

         }

 

         /** <Description functions in a word>

          * 执行保存/分析聊天记录动作

          * <Detail description>

          * @author Peter.Qiu

          * @param packet

          * @param incoming

          * @param processed

          * @param session [Parameters description]

          * @return void [Return type description]

          * @exception throws [Exception] [Exception description]

          * @see [Related classes#Related methods#Related properties]

          */

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

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

                                     // 群聊天,多人模式

                            } else if (message.getType() == Message.Type.groupchat) {

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

                                     // 其他信息

                            } else {                                 

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

                            }

                   } else if (packet instanceof IQ) {

                            IQ iq = (IQ) copyPacket;

                            if (iq.getType() == IQ.Type.set && iq.getChildElement() != null

                                               && "session".equals(iq.getChildElement().getName())) {

                                     System.out.println("用户登录成功Start======================");

                                     System.out.println("用户登录成功:"+iq.toXML());

                                     System.out.println("用户登录成功End=======================");

                            }

                   } else if (packet instanceof Presence) {

                            Presence presence = (Presence) copyPacket;

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

                                     System.out.println("用户退出服务器成功Start======================");

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

                                     System.out.println("用户退出服务器成功End========================");

                            }

                   }

         }

 

         /** <Description functions in a word>

          * 创建一个聊天记录实体对象,并设置相关数据

          * <Detail description>

          * @author Peter.Qiu

          * @param packet           数据包

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

          * @param session                   当前用户session

          * @return [Parameters description]

          * @return ChatLogs [Return type description]

          * @exception throws [Exception] [Exception description]

          * @see [Related classes#Related methods#Related properties]

          */

         private ChatLogs get(Packet packet, boolean incoming, Session session) {

                   //保留发送者信息.

                   /*if (packet == null || !incoming) {

                            return null;

                   }*/

                   Message message = (Message) packet;

                   ChatLogs logs = new ChatLogs();

                   JID jid = session.getAddress();

                   logs.setSender(jid.getNode());

                   JID recipient = message.getTo();

                   logs.setReceiver(recipient.getNode());

                   //自己发送的信息不用保存.

                   if (logs.getSender() != null && logs.getReceiver() != null

                                     && logs.getSender().equalsIgnoreCase(logs.getReceiver())) {

                            return null;

                   }

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

                   //解析body.json

                   //messageType: 聊天记录类型

                   //bodyContext:聊天记录内容

                   System.out.println("解析ParseBody聊天body Start=====================");

                   System.out.println("message.getBody():" + message.getBody());

                   Map<String, String> body = parseBody(message.getBody());

                   if (body != null) {

                            System.out.println("解析body.json");

                            logs.setMessageType(body.get(MESSAGE_TYPE));

                            logs.setContent(body.get(BODY_CONTEXT));

                   }

                   System.out.println("解析ParseBody聊天body End=====================");

                   return logs;

         }

 

         /** <Description functions in a word>

          * messageType: 聊天记录类型

          * bodyContext: 聊天记录内容

          * <Detail description>

          * @author Peter.Qiu

          * @param body

          * @return [Parameters description]

          * @return Map<String,String> [Return type description]

          * @exception throws [Exception] [Exception description]

          * @see [Related classes#Related methods#Related properties]

          */

         private Map<String, String> parseBody(String body) {

                   if (body == null || body.length() < 10) {

                            return null;

                   }

                   try {

                            JSONObject json = JSONObject.fromObject(body);

                            System.out.println("parseBody:"+ json);

                            if (json != null) {

                                     Map<String, String> bodyMap = new HashMap<String, String>();

                                     String messageType = json.getString(MESSAGE_TYPE);

                                     if (messageType == null || Integer.parseInt(messageType) < 0) {

                                               return null;

                                     }

                                     bodyMap.put(MESSAGE_TYPE, json.getString(MESSAGE_TYPE));

                                     bodyMap.put(BODY_CONTEXT, json.getString(BODY_CONTEXT));

                                     return bodyMap;

                            }

                   } catch (Exception e) {

                            System.out.println("parseBody转换JSON失败:"+ e);

                            return null;

                   }

                   return null;

         }

 

         /** <Description functions in a word>

          * 打印日志.

          * <Detail description>

          * @author Peter.Qiu

          * @param packet

          * @param incoming

          * @param processed

          * @param session [Parameters description]

          * @return void [Return type description]

          * @exception throws [Exception] [Exception description]

          * @see [Related classes#Related methods#Related properties]

          */

         private void debug(Packet packet, boolean incoming, boolean processed,

                            Session session) {

                   long start = System.currentTimeMillis();

                   System.out.println("###################debug start “"+start+"” ###################");

                   String info = "[ packetID: " + packet.getID() + ", to: "

                                     + packet.getTo() + ", from: " + packet.getFrom()

                                     + ", incoming: " + incoming + ", processed: " + processed

                                     + " ]";

                   System.out.println("id:" + session.getStreamID() + ", address: "

                                     + session.getAddress());

                   System.out.println("info: " + info);

                   System.out.println("xml: " + packet.toXML());

                   long end = System.currentTimeMillis();

                   System.out.println("###################debug end “"+end+"” #####################");

         }

 

 

         /** <Description functions in a word>

          *

          * <Detail description>

          * @author Peter.Qiu

          * @param args [Parameters description]

          * @return void [Return type description]

          * @exception throws [Exception] [Exception description]

          * @see [Related classes#Related methods#Related properties]

          */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

 

}

Plugin.java 插件入口

package com.qiuzhping.openfire.plugin;

 

import java.io.File;

 

import org.jivesoftware.openfire.container.Plugin;

import org.jivesoftware.openfire.container.PluginManager;

import org.jivesoftware.openfire.interceptor.InterceptorManager;

 

/**

 * <Description functions in a word>

 * 记录聊天信息插件

 * <Detail description>

 *

 * @author Peter.Qiu

 * @version  [Version NO, 2016年12月13日]

 * @see  [Related classes/methods]

 * @since  [product/module version]

 */

public class ChatLogsPlugin implements  Plugin {

        

         private InterceptorManager interceptorManager = InterceptorManager

                            .getInstance();

         private ChatLogsInterceptor chatLogsInterceptor;

 

         @Override

         public void destroyPlugin() {

                   interceptorManager.removeInterceptor(chatLogsInterceptor);

                   System.out.println("销毁聊天记录插件成功!");

         }

 

         @Override

         public void initializePlugin(PluginManager pluginManager,

                            File pluginDirectory) {

                   chatLogsInterceptor = new ChatLogsInterceptor();

                   interceptorManager.addInterceptor(chatLogsInterceptor);

                   System.out.println("安装聊天记录插件成功!");

         }

}

ChatLogs.java 聊天记录entity

/*

 * System Abbrev :

 * system Name  :

 * Component No  :

 * Component Name:

 * File name     :ChatLogs.java

 * Author        :Peter.Qiu

 * Date          :2016年12月12日

 * Description   :  <description>

 */

 

/* Updation record 1:

 * Updation date        :  2016年12月12日

 * Updator          :  Peter.Qiu

 * Trace No:  <Trace No>

 * Updation No:  <Updation No>

 * Updation Content:  <List all contents of updation and all methods updated.>

 */

package com.qiuzhping.openfire.plugin.entity;

 

import java.sql.Timestamp;

 

import org.jivesoftware.util.JiveConstants;

 

/**

 * <Description functions in a word>

 *  聊天内容实体.

 * <Detail description>

 *

 * @author Peter.Qiu

 * @version  [Version NO, 2016年12月13日]

 * @see  [Related classes/methods]

 * @since  [product/module version]

 */

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 表示删除

         private String messageType;//消息类型

         public interface LogState {

                   int show = 0;

                   int remove = 1;

         }

 

         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;

         }

 

         public long getMessageId() {

                   return messageId;

         }

 

         public void setMessageId(long messageId) {

                   this.messageId = messageId;

         }

 

         public String getSessionJID() {

                   return sessionJID;

         }

 

         public void setSessionJID(String sessionJID) {

                   this.sessionJID = sessionJID;

         }

 

         public String getSender() {

                   return sender;

         }

 

         public void setSender(String sender) {

                   this.sender = sender;

         }

 

         public String getReceiver() {

                   return receiver;

         }

 

         public void setReceiver(String receiver) {

                   this.receiver = receiver;

         }

 

         public Timestamp getCreateDate() {

                   return createDate;

         }

 

         public void setCreateDate(Timestamp createDate) {

                   this.createDate = createDate;

         }

 

         public String getContent() {

                   return content;

         }

 

         public void setContent(String content) {

                   this.content = content;

         }

 

         public String getDetail() {

                   return detail;

         }

 

         public void setDetail(String detail) {

                   this.detail = detail;

         }

 

         public int getLength() {

                   return length;

         }

 

         public void setLength(int length) {

                   this.length = length;

         }

 

         public int getState() {

                   return state;

         }

 

         public void setState(int state) {

                   this.state = state;

         }

 

         public String getMessageType() {

                   return messageType;

         }

 

         public void setMessageType(String messageType) {

                   this.messageType = messageType;

         }

}

 

plugin.xml 插件描述性文件

<?xml version="1.0" encoding="UTF-8"?>

<plugin>

    <class>com.qiuzhping.openfire.plugin.ChatLogsPlugin</class>

    <!-- Plugin meta-data -->

    <name>openfire聊天记录插件</name>

    <description>openfire用户聊天记录插件.</description>

    <author>http://blog.csdn.net/qiuzhping</author>

    <version>1.0.0</version>

    <date>13/12/2016</date>

    <url>http://localhost:9090/openfire/plugins.jsp</url>

    <minServerVersion>3.9.1</minServerVersion>

    <licenseType>gpl</licenseType>

</plugin>

3. build目录

build目录有两个文件分别是属性配置文件,ant打包文件

build.properties

app.path=E:/QiDa_Fixed_WorkSpace/OpenfirePlugin

plugin.name=ChatPlugin

plugin.path=E:/QiDa_Fixed_WorkSpace/OpenfirePlugin/src/plugins/ChatPlugin

build.xml

<project name="Webapp Precompilation" default="openfire-plugins" basedir=".">

         <property file="build.properties" />

         <property name="java.jar.dir" value="${app.path}/java-dist" />

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

         <target name="java-jar">

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

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

                            <fileset dir="${app.path}/bin" includes="**/*.class" />

                   </jar>

         </target>

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

         <target name="plug-jar">

                   <!-- 插件插件包相关lib -->

                   <mkdir dir="${app.path}/${plugin.name}/lib" />

                   <copy file="${java.jar}" todir="${app.path}/${plugin.name}/lib" />

                   <copy todir="${app.path}/${plugin.name}">

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

                   </copy>

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

                   <jar jarfile="${app.path}/${plugin.name}.jar">

                            <fileset dir="${app.path}/${plugin.name}" includes="**/**" />

                   </jar>

                   <delete dir="${app.path}/${plugin.name}" />

                   <delete dir="${app.path}/java-dist"/>

         </target>

 

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

         <target name="cleanALL">

                   <delete file="${app.path}/${plugin.name}.jar" />

                   <delete dir="${app.path}/${plugin.name}" />

                   <delete dir="${app.path}/java-dist"/>

         </target>

 

         <target name="openfire-plugin" depends="cleanALL,java-jar" />

         <target name="openfire-plugins" depends="cleanALL,java-jar,plug-jar" />

 

</project>

4.lib 其中7个jar都是JSON相关的

5.打包过程ChatPlugin.jar

6.插件数据库脚本

CREATE TABLE "ofchatlogs" (

  "messageid" bigint(20) NOT NULL AUTO_INCREMENT COMMENT '消息id',

  "sessionjid" varchar(30) DEFAULT NULL COMMENT '用户session jid名称',

  "sender" varchar(30) DEFAULT NULL COMMENT '消息发送者',

  "receiver" varchar(30) DEFAULT NULL COMMENT '接受者',

  "createdate" varchar(30) DEFAULT NULL COMMENT '消息发送、创建时间',

  "length" int(11) DEFAULT '0' COMMENT '消息长度、大小',

  "content" varchar(1024) DEFAULT NULL COMMENT '消息内容',

  "detail" varchar(4096) DEFAULT NULL COMMENT '消息源报文',

  "state" int(11) DEFAULT '0' COMMENT '删除状态,1表示删除',

  "message_type" int(8) DEFAULT '0' COMMENT '聊天记录类型',

  PRIMARY KEY ("messageid"),

  KEY "idx_message_type" ("message_type") USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=3400054 DEFAULT CHARSET=utf8;

插件安装效果:

Openfire插件开发实例

 

总结:Openfire的设计是模块化,插件化的,尽可能的解耦各功能之间的关联,通过各种XMPPServer实例来协调各种资源,这就非常巧妙和轻盈。通过这次开发插件,也基本熟悉了Openfire插件开发的基本流程,为以后开发更多Openfire插件打下基础。