• openfire spark 二次 开发 服务插件


    ====================  废话 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文件。

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

  • 相关阅读:
    PHP基础函数、自定义函数以及数组
    php 中 isset 和empty 的区别
    mysql中索引的使用
    mysql 中的增改查删(CRUD)
    mysql语句应该注意的问题
    《zw版·Halcon-delphi系列原创教程》 水果自动分类脚本(机器学习、人工智能)
    《zw版·Halcon-delphi系列原创教程》 2d照片-3d逆向建模脚本
    《zw版·Halcon-delphi系列原创教程》简单的令人发指,只有10行代码的车牌识别脚本
    《zw版·Halcon-delphi系列原创教程》航母舰载机·视觉定位标志的识别代码
    zw版_zw中文增强版Halcon官方Delphi例程
  • 原文地址:https://www.cnblogs.com/javaMan/p/4457208.html
Copyright © 2020-2023  润新知