zoukankan      html  css  js  c++  java
  • 开发XMPP IM

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

        本篇文章的结构如下:

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


    1、创建plugin.xml

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

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <plugin>
     3     <!-- Main plugin class  这里是最重要滴-->
     4     <class>com.im.server.plugin.GroupTreePlugin</class>
     5 
     6     <!-- Plugin meta-data -->
     7     <name>GroupTreePlugin</name>
     8     <description>This is the group plugin.</description>
     9     <author>Phoenix</author>
    10 
    11     <version>1.0</version>
    12     <date>14/03/2008</date>
    13     <url>http://localhost:9001/openfire/plugins.jsp</url>
    14     <minServerVersion>3.4.1</minServerVersion>
    15     <licenseType>gpl</licenseType>
    16 
    17     <!-- Admin console entries -->
    18     <adminconsole>
    19         <!-- More on this below -->
    20     </adminconsole>
    21 </plugin>

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

    2、实现Plugin 类和IQHandler

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

    GroupTreePlugin 类

     1 /**
     2  * 服务器端插件类
     3  * 
     4  * @author Phoenix
     5  * 
     6  * Mar 14, 2008 11:03:11 AM
     7  * 
     8  * version 0.1
     9  */
    10 public class GroupTreePlugin implements Plugin
    11 {
    12     private XMPPServer server;
    13 
    14     /*
    15      * (non-Javadoc)
    16      * 
    17      * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
    18      */
    19     public void destroyPlugin()
    20     {
    21 
    22     }
    23 
    24     /*
    25      * (non-Javadoc)
    26      * 
    27      * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager,
    28      *      java.io.File)
    29      */
    30     public void initializePlugin(PluginManager manager, File pluginDirectory)
    31     {
    32         PluginLog.trace("注册群组树IQ处理器");
    33         server = XMPPServer.getInstance();
    34         
    35         server.getIQRouter().addHandler(new GroupTreeIQHander()); //1
    36         server.getIQRouter().addHandler(new UserInfoIQHandler());
    37         server.getIQRouter().addHandler(new DelUserIQHandler());
    38         server.getIQRouter().addHandler(new CreateUserIQHandler());
    39         server.getIQRouter().addHandler(new AddGroupUserIQHandler());
    40         server.getIQRouter().addHandler(new SetRoleIQHandler());
    41     }
    42 }

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

    GroupTreeIQHander 类

     1 /**
     2  * 处理客户端发来的IQ,并回送结果IQ
     3  * 
     4  * @author Phoenix
     5  * 
     6  * Mar 14, 2008 4:55:33 PM
     7  * 
     8  * version 0.1
     9  */
    10 public class GroupTreeIQHander extends IQHandler
    11 {
    12     private static final String MODULE_NAME = "group tree handler";
    13     private static final String NAME_SPACE = "com:im:group";
    14     private IQHandlerInfo info;
    15 
    16     public GroupTreeIQHander()
    17     {
    18         super(MODULE_NAME);
    19         info = new IQHandlerInfo("gruops", NAME_SPACE);
    20     }
    21 
    22     /*
    23      * (non-Javadoc)
    24      * 
    25      * @see org.jivesoftware.openfire.handler.IQHandler#getInfo()
    26      */
    27     @Override
    28     public IQHandlerInfo getInfo()
    29     {
    30         return info;
    31     }
    32 
    33     /*
    34      * (non-Javadoc)
    35      * 
    36      * @see org.jivesoftware.openfire.handler.IQHandler#handleIQ(org.xmpp.packet.IQ)
    37      */
    38     @Override
    39     public IQ handleIQ(IQ packet) throws UnauthorizedException
    40     {
    41         IQ reply = IQ.createResultIQ(packet);
    42         Element groups = packet.getChildElement();//1
    43         
    44         if (!IQ.Type.get.equals(packet.getType()))
    45         {
    46             System.out.println("非法的请求类型");
    47             reply.setChildElement(groups.createCopy());
    48             reply.setError(PacketError.Condition.bad_request);
    49             return reply;
    50         }
    51         
    52         String userName = StringUtils.substringBefore(packet.getFrom().toString(),"@");
    53         GroupManager.getInstance().initElement(groups,userName);
    54         reply.setChildElement(groups.createCopy());//2
    55         System.out.println("返回的最终XML" + reply.toXML());
    56         return reply;
    57     }
    58 }

        可以看到主要有两个方法,一个是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 文件夹下:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project name="IM" default="release" basedir=".">
     3 
     4     <property name="openfire.path"
     5         value="E:/workspace/europa/openfire_src/target/openfire" />
     6     <property name="classes.dir" value="classes" />
     7     <property name="lib.dir" value="lib" />
     8 
     9     <target name="jar">
    10         <jar jarfile="${lib.dir}/grouptreeplugin.jar" basedir="${classes.dir}" >
    11             <fileset dir=".">
    12                 <include name="*.jar"/>
    13             </fileset>
    14         </jar>
    15         <jar jarfile="${openfire.path}/plugins/groupTreePlugin.jar">
    16             <fileset dir=".">
    17                 <include name="lib/*.jar" />
    18                 <include name="plugin.xml" />
    19                 <include name="logo_small.gif" />
    20                 <include name="logo_large.gif" />
    21                 <include name="readme.html" />
    22                 <include name="changelog.html" />
    23                 <include name="build.xml" />
    24             </fileset>
    25         </jar>
    26 
    27     </target>
    28 
    29     <target name="release" depends="jar">
    30     </target>
    31 
    32 </project>
  • 相关阅读:
    怎样装两个MySQL服务器
    MySQL 8.0.12的安装与卸载
    位运算符2
    位运算符
    赋值运算符
    love心形
    变量之间运算
    变量
    标识符
    算术运算符
  • 原文地址:https://www.cnblogs.com/eustoma/p/4265864.html
Copyright © 2011-2022 走看看