• openfire分析


    openfire开发文档
    版本:Openfire3.5.
    作者:---------- 日期:-----------

     

    一、初始工作····································2

    1、安装jdk1.5

    2、安装eclipse,myeclipse

    3、安装oracle10gXE

    4、安装openfire3.5.1源代码

    二、环境配置····································

    1、配置java环境

    2、配置openfire运行环境

    三、代码研究····································

    四、功能扩展····································

    五、与客户端一起调试··························


    第一章:初始工作

    1.1安装jdk1.5

    方法1:放在F盘的安装程序的java开发工具文件夹里

        方法2:可从www.sun.com.cn下载jdk1.5.

    1.2安装eclipse3.3,myeclipse6.0

        放在F盘的安装程序的java开发工具文件夹里

    1.3安装oracle10gXE

        放在F盘的安装程序的java开发工具文件夹里

    1.4安装openfire3.5.1源代码

        源代码的下载

    方法1:放在F盘的openfire文件夹里

    方法2:可从openfire的官方网站下载,网址在IE的收藏夹里

    ②源代码的安装

    将下载好的openfire源代码解压出来,复制到eclipseworkspace里,打开eclipse,点新建java工程,在Contents里选择第二个,即Create project from existing source, Directory选项里点右边的Browse按钮,选择eclipseworkspace里的openfire文件夹(这个文件夹的名字应该叫:openfire_src),点确定。再填入Project name,工程名字一定要和eclipseworkspace里的openfire源代码的文件夹名字相同,如下图所示:

    点击完成,即成功导入openfire源代码到eclipse中。如下图所示:

    右击工程,点属性->Java Build Path ->Libraries加入所对应的测试jar包,叫test什么什么的,放在E盘里。点击完成,即可将错误消除。

    第二章:环境配置

    2.1、配置java环境

        我的电脑点右键,选择“属性”。

    选择“高级”标签。

    进入环境变量设置:

    分别设置如下三个环境变量:
    PATH=C:"jdk1.6.0"bin
    CLASSPATH=.;%JAVA_HOME%"lib"tools.jar;%JAVA_HOME%"lib"dt.jar(注意,CLASSPATH最前面是有个“.”的,表示当前目录)
    JAVA_HOME=C:"jdk1.6.0

    设置完成之后,我们来测试一下。开始-》运行,输入“CMD”,回车。

    在打开的DOS命令窗口中输入“java -version”,回车。

    如果能像上图那样显示JDK的版本,说明“PATH”变量设置没有问题,如果有问题,检查PATH变量设置,输入“echo %PATH%”。

    看其中是否存在“C:"jdk1.6.0"bin”。如果不存在则返回到环境变量设置中检查。

    接下来我们编写一个简单的Java程序测试CLASSPATH是否设置正确。

    打开记事本,输入下面的内容,保存至C:"java目录下,文件名为HelloWorld.java。(注意大小写)

    class HelloWorld{
    public static void main(String[] arg){
    System.out.println("HelloWorld");
    }
    }

    在打开的DOS命令窗口,进入C:"java,输入“java HelloWorld.java”,回车。

    这时在C:"java目录下多了一个“HelloWorld.class”的文件。

    在打开DOS命令窗口,输入“java HelloWorld”,回车。

    如果程序没错,那么将输出“HelloWorld”。

    恭喜,你的Java环境配置成功了。

    2.2、配置openfire运行环境

        点击运行按钮旁边的小的黑色三角,点击Open Run Dialog…

    出现如下窗口,双击Java Application,Name框里的名字改成:openfire_src3.5.1,点击Main class右边的Search按钮,

     

     

    出现如下窗口:

    在第一个文本框里输入server,在第二个框里选择ServerStarter-org.jivesoftware.openfire.starter,点OK

     

    点击eclipse菜单Window->Show View->Ant,如图:

     

    ant窗口里右击选择Add Buildfiles…,如下图所示:

     

    出现如下窗口:选择:openfire_src->build->build.xml,点击OK按钮。

     

    双击在ant窗口里生成的菜单,即开始部署项目。

     

    察看console里显示的信息:如果显示BUILD SUCCESSFUL就表示项目部署成功。如图:

     

     

    第三章:代码研究

    Openfiresocket网络连接包括:

    <!--[if !supportLists]-->1.       <!--[endif]-->服务器和服务器之间的连接(监听在端口5269

    <!--[if !supportLists]-->2.       <!--[endif]-->外部组件和服务器之间的连接(监听在端口5275

    <!--[if !supportLists]-->3.       <!--[endif]-->多元(complex)连接(监听在端口5269

    <!--[if !supportLists]-->4.       <!--[endif]-->客户端和服务器的连接(监听在端口5222

    <!--[if !supportLists]-->5.       <!--[endif]-->和客户端通过TLS/SSL3.0和服务器的连接。(监听在端口5223

    这些连接都是通过ConnectionManager接口实现管理的,程序中对ConnectionManager接口的实现类是ConnectionManagerImpl,它是作为一个模块(Module)类加载到服务器中的。

    下面分析的是客户端和服务器的连接。

     

    ConnectionManagerImpl中是通过调用startClientListeners方法来初始化和开始端口监听的。

    Mina框架<!--[endif]-->startClientListeners方法使用的是ApacheMina框架来实现网络连接的,Mina框架的模式如下:

    IoFilter

           IoFilterMINA的功能扩展提供了接口。它拦截所有的IO事件进行事件的预处理和后处理。它与Servlet中的filter机制十分相似。多个IoFilter存放在IoFilterChain

           IoFilter能够实现以下功能:

                  数据转换

                  事件日志

                  性能检测

           Openfire中主要用filter这种机制来进行数据转换。

    Protocol Codec Factory

           Protocol Codec Factory提供了方便的Protocol支持,通过它的EncoderDecoder,可以方便的扩展并支持各种基于Socket的网络协议,比如HTTP服务器、FTP服务器、Telnet服务器等等。

           要实现自己的编码/解码器(codec)只需要实现interface: ProtocolCodecFactory即可,在Openfire中实现ProtocolCodecFactory的类为XMPPCodecFactory

    IoHandler:

        MINA中,所有的业务逻辑都有实现了IoHandlerclass完成    ,当事件发生时,将触发IoHandler中的方法:

               sessionCreated

    sessionOpened

    sessionClosed

    sessionIdle

    exceptionCaught

    messageReceived

    messageSent

           Openfire中客户端和服务器连接的IoHandler实现类是ClientConnectionHandler,它是从ConnectionHandler中继承来的。

    startClientListeners方法首先为Mian框架设置线程池,再将一个由XMPPCodecFactory作为Protocol Codec FactoryFilter放入到FilterChain中,然后绑定到端口5222,并将ClientConnectionHandler作为IoHandler对数据进行处理。完成这些步骤后Openfire就在5222等待客户端的连接。

    客户端连接的处理过程:

     

    当有客户端进行连接时根据Mina框架的模式首先调用的是sessionOpened方法。

           sessionOpened首先为此新连接构造了一个parserXMLLightWeightParser),这个parser是专门给XMPPDecoder(是XMPPCodecFactory的解码器类)使用的,再创建一个OpenfireConnection类实例connection和一个StanzaHandler的实例。最后将以上的parser, connectionStanzaHandler的实例存放在Minasession中,以便以后使用。

           当有数据发送过来时,Mina框架会调用messageReceived方法

           messageReceived首先从Minasession中得到在sessionOpened方法中创建的StanzaHandler实例handler,然后从parsers中得到一个parser(如果parsers中没有可以创建一个新的实例)(注意这个parser和在sessionOpened方法中创建的parser不同,这个parser是用来处理Stanza的,而在sessionOpened方法中创建的parser是在filter中用来解码的,一句话说就是在sessionOpened方法中创建的parser是更低一层的parser)。最后将xml数据包交给StanzaHander的实例hander进行处理。

           StanzaHander的实例hander处理xml数据包的过程

           StanzaHander首先判断xml数据包的类型,.如果数据包以“<stream:stream”打头那么说明客户端刚刚连接,需要初始化通信(符合XMPP协议)Openfire首先为此客户端建立一个与客户端JID相关的ClientSession,而后与客户端交互协商例如是否使用SSL,是否使用压缩等问题。当协商完成之后进入正常通信阶段,则可以将xml数据包交给这个用户的ClientSession进行派送(deliever),经过派送数据包可以发送给PacketRouteImpl模块进行处理。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    第四章:功能扩展

    4.1修改源代码

        用 户的创建。由于源代码中用户信息不能满足要求,需要扩展。服务器端创建用户比较简单,主要是客户端的创建比较复杂,主要有4个步骤,客户端发送创建用户的 请求(get请求),请求创建的用户字段,服务器端接收到之后进行过滤,然后返回信息(result),信息中只包含合法的用户属性,客户端收到之后,再 将合法的用户字段发送给服务器(set请求),服务器再根据收到的用户字段创建用户,即写入数据库。

    用户的查询。过程类似上面用户的创建。

    4.2插件开发

    Openfire 服务器端是支持插件开发的,开发过程可能会涉及到数据库的操作,本篇文章专注于Openfire 插件的部分,对服务器端涉及到数据库的开发只做简单介绍。

    Openfire
    是一个用Java 实现的XMPP 服务器,客户端可以通过IQ 的方式与其进行通信(其实就是XML),客户端和服务器之间的通信是依靠底层Smack 库提供的各种功能来完成的。其实利用插件方式来扩展Openfire 服务器端主要有两种扩展方式,一种是对服务器控制台页面进行扩展(不是本文的主要内容),其实就是遵循Openfire 页面的布局方式,进行相应的页面扩展和功能扩展;另一种是对通信功能进行扩展。本文主要针对后者进行具体的描述

    本篇文章的结构如下:

    1
    、创建plugin.xml(这是整个插件最关键的文档)
    2
    、创建服务器插件实例(实现Plugin 接口的一个类还有一批IQHandler
    3
    、打包插件(Openfire 插件也有自己的打包方式)和部署插件

    好滴,实刀实枪的来动手做吧

    1
    、创建plugin.xml

    初次开发Openfire Spark 插件的时候,很容易把二者搞混,千万记得,这里是Openfire plugin.xml 不是第二篇文章说的那个啦!

    <?xml version="1.0" encoding="UTF-8"?>
    <plugin>
        <!-- Main plugin class 
    这里是最重要滴-->
        <class>com.im.server.plugin.GroupTreePlugin</class>

        <!-- Plugin meta-data -->
        <name>GroupTreePlugin</name>
        <description>This is the group plugin.</description>
        <author>Phoenix</author>

        <version>1.0</version>
        <date>14/03/2008</date>
        <url>http://localhost:9001/openfire/plugins.jsp</url>
        <minServerVersion>3.4.1</minServerVersion>
        <licenseType>gpl</licenseType>

        <!-- Admin console entries -->
        <adminconsole>
            <!-- More on this below -->
        </adminconsole>
    </plugin>

    最重要的那一行我已经标记出来啦,就是你这个插件的初始化和垃圾清理类,例子中是在com.im.server.plugin 包中的GroupTreePlugin 类,下文会对这个类进行详细描述。其余的都是描述信息,只要你提供了正确的描述信息,一般都不会出错。建议初次开发者,在写完plugin.xml 文件后,写一个简单的Plugin 实例,并打印出一些信息,如果重新启动Openfire 信息成功显示,恭喜你,你已经迈出一大步了!

    2
    、实现Plugin 类和IQHandler

    Plugin
    类主要起到的作用是初始化和释放资源,在初始化的过程中,最重要的的注册一批IQHandlerIQHander 的作用有点类似于Spark 中的IQProvider,其实就是解析XML 文件之后,生成一些有用的实例,以供处理。下面分别给出一个Plugin 类的实例和IQProvider 的实例

    GroupTreePlugin


    /**
     *
    服务器端插件类
     *
     * @author Phoenix
     *
     * Mar 14, 2008 11:03:11 AM
     *
     * version 0.1
     */
    public class GroupTreePlugin implements Plugin
    {
        private XMPPServer server;

        /*
         * (non-Javadoc)
         *
         * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
         */
        public void destroyPlugin()
        {

        }

        /*
         * (non-Javadoc)
         *
         * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager,
         *      java.io.File)
         */
        public void initializePlugin(PluginManager manager, File pluginDirectory)
        {
            PluginLog.trace("
    注册群组树IQ处理器");
            server = XMPPServer.getInstance();
           
            server.getIQRouter().addHandler(new GroupTreeIQHander()); //1
            server.getIQRouter().addHandler(new UserInfoIQHandler());
            server.getIQRouter().addHandler(new DelUserIQHandler());
            server.getIQRouter().addHandler(new CreateUserIQHandler());
            server.getIQRouter().addHandler(new AddGroupUserIQHandler());
            server.getIQRouter().addHandler(new SetRoleIQHandler());

        }

    }

    上例所示,在初始化中先找到IQRouter,然后通过IQRouter 注册一批IQHandler,这些IQHander 会自动监听相应命名空间的IQ,然后进行处理;由于这个Plugin 不需要做资源释放的工作,所以在destroyPlugin() 方法中没有任何内容。具体的IQHander 类如下

    GroupTreeIQHander

    /**
     *
    处理客户端发来的IQ,并回送结果IQ
     *
     * @author Phoenix
     *
     * Mar 14, 2008 4:55:33 PM
     *
     * version 0.1
     */
    public class GroupTreeIQHander extends IQHandler
    {

        private static final String MODULE_NAME = "group tree handler";

        private static final String NAME_SPACE = "com:im:group";

        private IQHandlerInfo info;

        public GroupTreeIQHander()
        {
            super(MODULE_NAME);
            info = new IQHandlerInfo("gruops", NAME_SPACE);
        }

        /*
         * (non-Javadoc)
         *
         * @see org.jivesoftware.openfire.handler.IQHandler#getInfo()
         */
        @Override
        public IQHandlerInfo getInfo()
        {
            return info;
        }

        /*
         * (non-Javadoc)
         *
         * @see org.jivesoftware.openfire.handler.IQHandler#handleIQ(org.xmpp.packet.IQ)
         */
        @Override
        public IQ handleIQ(IQ packet) throws UnauthorizedException
        {
            IQ reply = IQ.createResultIQ(packet);
            Element groups = packet.getChildElement();//1
           
            if (!IQ.Type.get.equals(packet.getType()))
            {
                System.out.println("
    非法的请求类型");
                reply.setChildElement(groups.createCopy());
                reply.setError(PacketError.Condition.bad_request);
                return reply;
            }
           
            String userName = StringUtils.substringBefore(packet.getFrom().toString(),"@");

            GroupManager.getInstance().initElement(groups,userName);
           
            reply.setChildElement(groups.createCopy());//2

            System.out.println("
    返回的最终XML" + reply.toXML());

            return reply;
        }

    }

    可以看到主要有两个方法,一个是getInfo() 这个方法的目的是提供要解析的命名空间,在本例中,这个IQHandler 对每个命名空间为"com:im:group" 的实例进行处理;还有一个最重要的方法:handleIQ() 该方法对包含指定命名空间的XML 进行解析,然后返回一个解析好的IQ。其实我认为,这个IQHandler IQ 的关系就是Controller Model 的关系(如果你了解MVC 的话,那么你一定知道我再说什么),只不过这里并没有指定什么View,你完全可以把IQ 当成Model 类进行理解。在这里,我用了GroupManager 进行了XML 的处理,因为我返回的IQ 内容中要从数据库读取所有群组信息,所以转交给GroupManager 进行处理,你完全可以在这个方法中进行具体的XML 处理,在这里,解析和创建新的XML 主要用到的是JDOM(如果你对Java 解析XML 有所了解,那真的太好了!)。程序//1 处主要是获取创建返回的IQ,并获取原来IQ 的子元素(用于创建我们返回的IQ);程序//2 处很关键,如果你不调用createCopy 方法,程序会出错(程序会死锁还是什么,忘记咧,不好以西)。

    这就是程序的主体部分,我在这里有一个建议,能不用Openfire 原始的程序函数,就不要用它们。我的提取数据库方式都是自己写的Bean,这样有利于你自己对程序的掌控,其实更有利于快速开发(这世道不是啥都讲究敏捷么,哇哈哈)

    3
    、打包插件

    打包依然遵循二次打包的原则(如果你不了解啥叫要二次打包,请看上一篇)
    这是我的ant 文件,由于Eclipse 帮我做了build 等很多工作,实际我的ant 工作就是在打包,并放入插件目录下的plugin 文件夹下

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="IM" default="release" basedir=".">

        <property name="openfire.path"
            value="E:/workspace/europa/openfire_src/target/openfire" />
        <property name="classes.dir" value="classes" />
        <property name="lib.dir" value="lib" />

        <target name="jar">
            <jar jarfile="${lib.dir}/grouptreeplugin.jar" basedir="${classes.dir}" >
                <fileset dir=".">
                    <include name="*.jar"/>
                </fileset>
            </jar>
            <jar jarfile="${openfire.path}/plugins/groupTreePlugin.jar">
                <fileset dir=".">
                    <include name="lib/*.jar" />
                    <include name="plugin.xml" />
                    <include name="logo_small.gif" />
                    <include name="logo_large.gif" />
                    <include name="readme.html" />
                    <include name="changelog.html" />
                    <include name="build.xml" />
                </fileset>
            </jar>

        </target>

        <target name="release" depends="jar">
        </target>

    </project>

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    第五章:与客户端一起调试

    5.1debug模式启动服务器,根据客户端的需要在相应位置设置端点,分析数据。


    很好的个人总结:http://phoenixtoday.blogbus.com/index_2.html

  • 相关阅读:
    Spyder 快捷键大全
    上传代码到github,出现 git@github.com: Permission denied (publickey) 错误
    Linux ubuntu 安装 openssh-server 报错
    win10删除「此电脑」中的文档、视频、音乐、下载、图片、桌面等6个文件夹的方法
    IDEA 光标闪烁问题
    委托、Action泛型委托、Func泛型委托、Predicate泛型委托的用法
    C#6.0的新语法特性
    创建多个网站
    发送短信功能(C#)
    VS开发Windows服务
  • 原文地址:https://www.cnblogs.com/avenxia/p/2279866.html
Copyright © 2020-2023  润新知