====================  废话 begin   ============================

最近老大让我为研发平台增加即时通讯功能。告诉我用comet 在web端实现即时通讯。

最初狂搜集资料。不能让自己方向错了。这是很重要的。

不过还是难免的周折了一番。测试了一个comet4j的聊天小例子。用它前后端开发成本太大、对服务器也太大压力放弃了。

最终决定使用openfire +jsjac.js + JabberHTTPBind 然后实现老大要求的 web 及时通讯功能。

很庆幸找到了 hoojo大哥的demo 很不幸,他为了让大家复制代码,自己练习。不提供jar包js下载。(虽然好心但是足足浪费了我两天时间)一个jsjac.js库版本有问题。很费劲的看源码。哎。

然后、拿这个小demo 先交差。顺便展示了 spark  和 web聊天窗口,交互即时聊天。

顺便构想了一下,修改openfire用户表。让用户来自系统。组织则用自带的。(当然这是预想。其实openfire已经帮我们想过了。)

接着、全局搜索了openfire源码中包含ofuser表sql的类。只有两个类。很庆幸。改了之后,改造用户密码加密认证方式。

当然这样做是错误的。直到我发现类名字似乎有些不对的时候。DefaultUserProvider  哈哈、嘲讽啊。 不出所料有一个实现类JDBCUuserProvider 。直接配置就可以搞定、但是加密sha256加密过程和我平台不一样。改造后就顺利搞定。

接着、到了插件开发过程。这个过程很烦人。网上很多帖子很多人去讲这个开发过程。或许是两三年前的贴了。很多过时了。只能有一点帮助。更多的是走向了错误的道路。磕磕碰碰。最终还是只能从源码中寻求出路。

这个过程整整浪费了我两星期时间。很痛苦。所以。我会针对最新代码聊聊,spark 开发一个组织架构树插件。展示出用户。并可以与之聊天。打包openfire,spark插件过程。打包项目为exe文件。等

=======================  废话 end  =========================

openfire10,出来啦。^_^  支持自定义组,性能优化很多。

本文内容有:

  1、openfire自定义用户表 需要注意的地方

  2、openfire服务器插件开发 (开发环境搭建不说了。下载项目,目前3.10,3.93都没有任何错)

  3、openfire插件打包。

  3、spark插件开发

  4、openfire/spark 打包exe

  

一、Openfire自定义用户表

userManager 会在初始化的时候从数据库读取UserProvider/AuthProvider的实现类。默认是defaultUserProvider

这些实现类参数被保存在ofproperty 表中。我们直接通过更新表完成对自定义用户表的配置。

insert  into ofProperty(name,propValue)values
('jdbcProvider.driver','com.mysql.jdbc.Driver'),
('jdbcProvider.connectionString','jdbc:mysql://主机地址/数据库?user=root&password=root'),
--用户表数据库连接信息
('admin.authorizedJIDs','admin@127.0.0.1'),
--管理员账号信息,@amy-tang是安装openfire服务器时填写的IP,也可以写成服务器的域名。
('jdbcAuthProvider.passwordSQL','SELECT password FROM sys_user WHERE account=?'),
-- 校验用户名密码sql语句
('jdbcAuthProvider.passwordType','sha256'),-- 用户表加密方式
('jdbcUserProvider.loadUserSQL','SELECT fullname, email FROM sys_user WHERE account=?'),
-- 查询用户的sql语句
('jdbcUserProvider.userCountSQL','SELECT COUNT(*) FROM sys_user'),
('jdbcUserProvider.allUsersSQL','SELECT account FROM sys_user'),
('jdbcUserProvider.usernameField','account'),
('jdbcUserProvider.nameField','fullname'),
('jdbcUserProvider.emailField','Email'),
--用户表关键字段的字段名称。模糊查询用户拼装sql使用.account是账号唯一标示,fullname是用户名。sys_user是用户表 UPDATE ofProperty SET propValue='org.jivesoftware.openfire.user.JDBCUserProvider' WHERE name='provider.user.className' ;
--设置用户数据库持久层实现类
UPDATE ofProperty SET propValue='org.jivesoftware.openfire.auth.JDBCAuthProvider' WHERE name='provider.auth.className'
--设置密码校验持久层实现类

注意事项:1、加密key  :plain/md5/sha1/sha256/sha512  加密过程详见org.jivesoftware.util.StringUtils.hash(password, "MD5");

     2、admin.authorizedJIDs   = admin@主机地址    当Openfire修改了主机地址/域名的配置后admin无法登陆服务器。需要修改数据库中配置的主机地址,

二、Openfire服务器插件开发

1、openfire中插件目录介绍,以及开发前准备。

Openfire源码环境搭建很简单。目前3.10版,3.9版都没有任何问题直接能用,不用多余下载任何jar。将源码解压至工作空间,新建相同项目名称即可。如图:

暂时没用的插件可以  build path remove from path先从工作空间移除。剩下需要的开发插件如上图右

10 的source output folder 有点问题,指向了一个插件里面。修改即可。

项目右键—》build path—》configure build path..—》 Source—》output folder: openfire_src/bin

9问题也不多,网上很多文章都很有用可以检索 openfire二次开发去查阅相关搭建开发环境的文章。

开发前,建议先吧xmldebugger 插件打开,或者留在src 中。会把所有交互xml打印输出非常有利于调试。

开发插件前,我们可以先看下,自带的那些插件的目录结构。

可以看出来开发插件都是依赖项目开发的,这样直接可以引用项目中的资源,bean。不建议单独起一个project去开发插件

2、插件开发介绍

例如组织架构插件,我是将它作为一个服务组件进行开发,对外提供组织树的服务。当客户端访问服务器 jabber:iq:loadOrg 的服务的时候,根据提供的参数返回组织架构,与用户信息、

该插件需要实现两个接口 Component, Plugin。 贴下代码

package com.hotent.openfire.plugin;

import java.io.File;
import java.util.List;
import java.util.Map; import net.sf.json.JSONArray; import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.DefaultAuthProvider;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet; public class OrgTreePlugin implements Component, Plugin {
private static final Logger Log = LoggerFactory.getLogger(OrgTreePlugin.class); public static final String NAMESPACE_JABBER_IQ_SEARCH = "jabber:iq:loadOrg";
public static final String SERVICENAME = "plugin.loadOrg.serviceName"; private UserManager userManager;
private PluginManager pluginManager;
private ComponentManager componentManager;
private String serviceName = "loadOrg";
private String serverName ;
private OrgTreeProvider orgTreeProvider; // 插件初始化的时候,注入bean。
public OrgTreePlugin(){
userManager = UserManager.getInstance();
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
orgTreeProvider = DefaultOrgTreeProvider.getInstance();
} public String getName() {
return pluginManager.getName(this);
} public String getDescription() {
return pluginManager.getDescription(this);
}
// 初始化插件调用的方法。此时注册插件服务。
@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
pluginManager = manager;
componentManager = ComponentManagerFactory.getComponentManager();
try {
//注册服务。
componentManager.addComponent(serviceName, this);
} catch (ComponentException e) {
Log.error("===================OrgTree 注册服务失败!!=====================================");
throw new RuntimeException("OrgTree 注册服务失败!!",e);
}
Log.info("===================OrgTree 注册服务成功!!====================================="); } public void initialize(JID jid, ComponentManager componentManager) {
} public void start() {
} public void destroyPlugin() {
pluginManager = null;
try {
componentManager.removeComponent(serviceName);
componentManager = null;
} catch (Exception e) { }
serviceName = null;
userManager = null;
} @Override
public void shutdown() {
}
// 当客户端请求该服务时,访问该方法,接下来处理消息包
//第一次访问注册服务时候, 也会测试性质的访问该服务。如果出现异常,将从服务列表移除
//
@Override
public void processPacket(Packet p) {
if (!(p instanceof IQ)) {
return;
}
final IQ packet = (IQ) p; if (packet.getType().equals(IQ.Type.error) || packet.getType().equals(IQ.Type.result)) {
return;
}
//将消息包处理,并将结果数据,返回给请求者。
final IQ replyPacket = reply(packet); try {
componentManager.sendPacket(this, replyPacket);
} catch (ComponentException e) {
} }
/***
* 处理消息包,转成回执包
* @param packet
* @return
*/
private IQ reply(IQ packet) {
/*if (!packet.getType().equals(IQ.Type.get)) {
throw new IllegalArgumentException("This method only accepts 'get' typed IQ stanzas as an argument.");
}*/
String orgId ="";
final Element element = packet.getChildElement();
if(element != null){
Element e = element.element("orgId");
if(e!= null)orgId = e.attribute("id").getText();
} String authId = packet.getFrom().toString();
if(authId .contains("@"))
authId = authId.substring(0, authId.indexOf("@")); IQ replyPacket = IQ.createResultIQ(packet); Element queryResult = DocumentHelper.createElement(QName.get("query", NAMESPACE_JABBER_IQ_SEARCH));
//去数据库获取数据
List<OrgTreeNode> orgTreeList = orgTreeProvider.LoadOrgChildOrgUser(orgId, authId);
//orgTreeList.remove(orgTreeList.size()-1);
JSONArray json = JSONArray.fromObject(orgTreeList); queryResult.addElement("orgTreeList").addText(json.toString());
replyPacket.setChildElement(queryResult); return replyPacket;
} }

OrgTreePlugin 代码,

请看打开代码自己查看。该插件需要实现两个接口Component, Plugin,并实现相应方法。

插件的初始在PluginManager中419 行。 可以断点查看插件初始化过程。

419                String className = pluginXML.selectSingleNode("/plugin/class").getText().trim();
420 plugin = (Plugin)pluginLoader.loadClass(className).newInstance();

服务注册也很简单 如:OrgTreePlugin 代码initializePlugin 方法。

componentManager.addComponent(serviceName, this);

只是需要注意的是,该服务注册为服务组件的时候,会测试的访问该服务,测试是否正常。

如果出现异常就会自动 调用componentManager.removeComponent(serviceName);,将改服务移除服务组件。

具体代码可以查看InternalComponentManager 的addComponent()方法。代码很简单。

DefaultOrgTreeProvider则为数据访问层。不再贴了。

插件目录结构如下:

3、插件打包

最初在看openfire插件开发相关文章的时候、都提供了很多打包的方法,不过用着很不开心。

其实项目已经提供了所有各个功能的ant脚本build目录下 build.xml文件。

三、spark插件开发


spark插件开发与openfire大略相似,

需要自身了解 AWT相关的知识。

打包插件需要注意一点在 build.xml 中 target name="build.plugins" 添加自己插件。这样运行  ant  build.plugins 的时候就可以打包自己的jar 了

build.plugins

    <subant target="">
<fileset dir="./src/plugins/orgTree/" includes="*/build.xml" />
</subant>

获取请求服务,处理数据关键代码

//请求消息 定义
public class OrgTreeLoad extends IQ private JSONArray getOrgTreeJSON(String orgId) throws XMPPException {
//获取host
Connection con = SparkManager.getConnection();
host = con.getHost(); OrgTreeLoad search = new OrgTreeLoad();
search.setType(org.jivesoftware.smack.packet.IQ.Type.SET);
search.setTo("loadOrg."+host); //消息包处理
search.addExtension(OrgTreePacketExtension.getNewPacketExtension(orgId));
PacketCollector collector = con.createPacketCollector(new PacketIDFilter(search.getPacketID()));
con.sendPacket(search); IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
collector.cancel();
//请求超时
if(response == null)
throw new XMPPException("No response from server on status set.");
//出错
if(response.getError() != null)
return null;//
else{
       //response 也是类似IQ的消息包,xml格式,获取返回的数据,并处理。
String xml = response.getChildElementXML();
if(xml ==null) return null;
try {
Document d = DocumentHelper.parseText(xml);
if(d!= null) xml =d.getRootElement().getText();
} catch (DocumentException e) {
e.printStackTrace();
}
//
return JSONArray.fromObject(xml);
}
}

打开一个聊天窗口,发起聊天

   private void openChatRoom(String authId,String name,String orgName) {
ChatManager chatManager = SparkManager.getChatManager();
ChatRoom chatRoom = chatManager.createChatRoom(authId+"@"+host, orgName+" :"+name,name); ChatContainer chatRooms = chatManager.getChatContainer();
chatRooms.activateChatRoom(chatRoom);
}

spark 自己顺着登陆代码,跟进去、慢慢看。看如何加载所有面板,加载用户信息,好友信息。搜索好友面板。等等。代码都可以看的懂的。

实在找不到具体实现代码 可以先找到中文spark_i18n_zh_CN.properties, 然后通过对应的res Key找全文搜索,找到 Res.getString("menuitem.bookmark.room")

就能代码定位了。百试不爽。

其他就不赘述,篇幅精力有限。

4、openfire/spark 打包exe

在build /installer 下 spark/openfire.install4j  下载了install4j   破解后,直接双击,即可。或者配置下install4j  目录直接运行ant命令installer

openfire.install4j 文件配置了打包exe所有的配置、可以自己根据需要进行修改。打包成自己需要的exe文件。

自己摸索吧。貌似都少不了磕磕碰碰一番。

openfire spark 二次 开发 服务插件的更多相关文章

  1. PC结束 Spark 二次开发 收到自己主动,并允许好友请求

    本次Spark二次开发是为了客服模块的开发, 能让用户一旦点击该客服则直接自己主动加入好友.而客服放则需自己主动加入好友,不同弹出对话框进行允许,这方便的广大客服. 如今废话不多说,直接上代码. pa ...

  2. 北京智和信通IT运维管理系统二次开发服务提供商

    随着云计算.大数据.物联网.移动互联网.人工智能.5G等高新技术的快速发展,数据中心及网络基础设施呈现出井喷式的增长模式,对设备商来说,多.快.好.省的实现定制化网络管理开发,可极大的扩充设备适用范围 ...

  3. 【工业串口和网络软件通讯平台(SuperIO)教程】七.二次开发服务驱动

    SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1    服务接口的作用 围绕着设备驱动模块采集的数据,根据需求提供多种应用服务,例如:数据上传服务.数 ...

  4. 使用C#语言,如何实现EPLAN二次开发 Api插件及菜单展示

    上期我们谈谈了谈EPLAN电气制图二次开发,制图软件EPLAN的安装和破解,今天我们来说说使用C#语言,如何实现Api插件及菜单,今天它来了!!! 关于项目环境的搭建请参考:https://blog. ...

  5. 如何开发jQuery插件

    一:普及JQuery知识 知识1:用JQuery写插件时,最核心的方法有如下两个: $.extend(object) 可以理解为,为JQuery 类添加一个静态方法. $.fn.extend(obje ...

  6. 3.NetDh框架之缓存操作类和二次开发模式简单设计(附源码和示例代码)

    前言 NetDh框架适用于C/S.B/S的服务端框架,可用于项目开发和学习.目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型.多库实例,简单强大: 此部分具体说明可参考博客: ...

  7. 超强在线考试系统源码(私有部署&二次开发)

    随着信息化技术的发展,考试系统也在进行着深入的变革.从传统的纸质考试人工评分到现在的在线考试自动评分. 在线考试系统的应用场景也在逐渐扩宽,例如:学校的学生考试.员工培训考试.招聘考试.职称考试等等. ...

  8. EasyNVR摄像机网页H5全平台无插件直播流媒体播放服务二次开发之接口鉴权示例讲解

    背景需求 EasyNVR的使用者应该都清楚的了解到,EasyNVR一个强大的功能就是可以进行全平台的无插件直播.主要原因在于rtsp协议的视频流(默认是需要插件才可以播放的)经由EasyNVR处理可以 ...

  9. 基于EasyNVR摄像机网页无插件直播服务二次开发实现H5播放页面的简单集成方案

    我们通常在构架一套视频SaaS应用的过程中,将平台设计为3层:视频硬件层(视频源).视频能力平台(vPaaS).视频应用平台(vSaaS),视频硬件包括各种IPC.NVR.编码器等视频生成设备,vPa ...

随机推荐

  1. display:inline,display:inline-block,display:block 区别

    之前一直迷惑于display:inline/inline-block/block的异同,在度娘谷哥的帮助下,突然有了一点思路. 按照网上的介绍,inline将对象转化为内联元素,block将对象转化为 ...

  2. jq仿虾米网flash效果

    这是很久以前写的一个效果了,之前虾米音乐网首页的一个flash效果,最初觉得这flash效果也可以完全用jq来写,于是空余时间就写了下当作练习吧,现在就拿出来跟大家分享下其中的实现原理! 先上最终效果 ...

  3. 由setTimeout()里的this引出的this

    example 1: window.id='windowid'; function M(){ this.id='Mid'; this.f1=function(){console.log(this.id ...

  4. 如何解析复杂的C语言声明

    C语言中有时会出现复杂的声明,比如   char * const * (*next) (); //这是个什么东东?   在讲复杂声明的分析方法前,先来个补充点.   C语言变量的声明始终贯彻两点 :  ...

  5. C#数字图像处理的3种方法

    本文主要通过彩色图象灰度化来介绍C#处理数字图像的3种方法,Bitmap类.BitmapData类和Graphics类是C#处理图像的的3个重要的类. Bitmap只要用于处理由像素数据定义的图像的对 ...

  6. UIBezierPath

    UIBezierPath 笔者在写本篇文章之前,也没有系统学习过贝塞尔曲线,只是曾经某一次的需求需要使用到,才临时百度看了一看而且使用最基本的功能.现在总算有时间停下来好好研究研究这个神奇而伟大的贝塞 ...

  7. 更改xcode上iphone模拟器颜色的方法--备用

    到模拟器的目录下修改图片即可——在Finder中显示,显示模拟器包内容,修改Contents/Resources/frame.png图片!

  8. 51单片机C语言学习笔记6:51单片机C语言头文件及其使用

    很多初学单片机者往往对C51的头文件感到很神秘,而为什么要那样写,甚至有的初学者喜欢问,P1口的P为什么要大写,不大写行不行呢?其实这个是在头文件中用sfr定义的,现在定义好了的是这样的 sfr P1 ...

  9. windows 触发桌面图标布局保存

    问题: 项目原有的一套结构由于引进了一个磁盘套件,类似于关闭系统的explorer.exe进程,进入到他所维护的explorer.exe中.于是出现了当退出磁盘的时候没有保存好桌面布局信息导致下次进入 ...

  10. 高级私人定制西服品牌:XUAN PRIVE 为定制而生_乐活_onlylady女人志

    高级私人定制西服品牌:XUAN PRIVE 为定制而生_乐活_onlylady女人志 高级私人定制西服品牌:XUAN PRIVE 为定制而生